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) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6247 return;
6248 };
6249
6250 let mut best_length = usize::MAX;
6251 let mut best_inside = false;
6252 let mut best_in_bracket_range = false;
6253 let mut best_destination = None;
6254 for (open, close) in enclosing_bracket_ranges {
6255 let close = close.to_inclusive();
6256 let length = close.end() - open.start;
6257 let inside = selection.start >= open.end && selection.end <= *close.start();
6258 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6259
6260 // If best is next to a bracket and current isn't, skip
6261 if !in_bracket_range && best_in_bracket_range {
6262 continue;
6263 }
6264
6265 // Prefer smaller lengths unless best is inside and current isn't
6266 if length > best_length && (best_inside || !inside) {
6267 continue;
6268 }
6269
6270 best_length = length;
6271 best_inside = inside;
6272 best_in_bracket_range = in_bracket_range;
6273 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6274 if inside {
6275 open.end
6276 } else {
6277 open.start
6278 }
6279 } else {
6280 if inside {
6281 *close.start()
6282 } else {
6283 *close.end()
6284 }
6285 });
6286 }
6287
6288 if let Some(destination) = best_destination {
6289 selection.collapse_to(destination, SelectionGoal::None);
6290 }
6291 })
6292 });
6293 }
6294
6295 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6296 self.end_selection(cx);
6297 self.selection_history.mode = SelectionHistoryMode::Undoing;
6298 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6299 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6300 self.select_next_state = entry.select_next_state;
6301 self.select_prev_state = entry.select_prev_state;
6302 self.add_selections_state = entry.add_selections_state;
6303 self.request_autoscroll(Autoscroll::newest(), cx);
6304 }
6305 self.selection_history.mode = SelectionHistoryMode::Normal;
6306 }
6307
6308 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6309 self.end_selection(cx);
6310 self.selection_history.mode = SelectionHistoryMode::Redoing;
6311 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6312 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6313 self.select_next_state = entry.select_next_state;
6314 self.select_prev_state = entry.select_prev_state;
6315 self.add_selections_state = entry.add_selections_state;
6316 self.request_autoscroll(Autoscroll::newest(), cx);
6317 }
6318 self.selection_history.mode = SelectionHistoryMode::Normal;
6319 }
6320
6321 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6322 self.go_to_diagnostic_impl(Direction::Next, cx)
6323 }
6324
6325 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6326 self.go_to_diagnostic_impl(Direction::Prev, cx)
6327 }
6328
6329 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6330 let buffer = self.buffer.read(cx).snapshot(cx);
6331 let selection = self.selections.newest::<usize>(cx);
6332
6333 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6334 if direction == Direction::Next {
6335 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6336 let (group_id, jump_to) = popover.activation_info();
6337 if self.activate_diagnostics(group_id, cx) {
6338 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6339 let mut new_selection = s.newest_anchor().clone();
6340 new_selection.collapse_to(jump_to, SelectionGoal::None);
6341 s.select_anchors(vec![new_selection.clone()]);
6342 });
6343 }
6344 return;
6345 }
6346 }
6347
6348 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6349 active_diagnostics
6350 .primary_range
6351 .to_offset(&buffer)
6352 .to_inclusive()
6353 });
6354 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6355 if active_primary_range.contains(&selection.head()) {
6356 *active_primary_range.end()
6357 } else {
6358 selection.head()
6359 }
6360 } else {
6361 selection.head()
6362 };
6363
6364 loop {
6365 let mut diagnostics = if direction == Direction::Prev {
6366 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6367 } else {
6368 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6369 };
6370 let group = diagnostics.find_map(|entry| {
6371 if entry.diagnostic.is_primary
6372 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6373 && !entry.range.is_empty()
6374 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6375 {
6376 Some((entry.range, entry.diagnostic.group_id))
6377 } else {
6378 None
6379 }
6380 });
6381
6382 if let Some((primary_range, group_id)) = group {
6383 if self.activate_diagnostics(group_id, cx) {
6384 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6385 s.select(vec![Selection {
6386 id: selection.id,
6387 start: primary_range.start,
6388 end: primary_range.start,
6389 reversed: false,
6390 goal: SelectionGoal::None,
6391 }]);
6392 });
6393 }
6394 break;
6395 } else {
6396 // Cycle around to the start of the buffer, potentially moving back to the start of
6397 // the currently active diagnostic.
6398 active_primary_range.take();
6399 if direction == Direction::Prev {
6400 if search_start == buffer.len() {
6401 break;
6402 } else {
6403 search_start = buffer.len();
6404 }
6405 } else if search_start == 0 {
6406 break;
6407 } else {
6408 search_start = 0;
6409 }
6410 }
6411 }
6412 }
6413
6414 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6415 let snapshot = self
6416 .display_map
6417 .update(cx, |display_map, cx| display_map.snapshot(cx));
6418 let selection = self.selections.newest::<Point>(cx);
6419
6420 if !self.seek_in_direction(
6421 &snapshot,
6422 selection.head(),
6423 false,
6424 snapshot
6425 .buffer_snapshot
6426 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6427 cx,
6428 ) {
6429 let wrapped_point = Point::zero();
6430 self.seek_in_direction(
6431 &snapshot,
6432 wrapped_point,
6433 true,
6434 snapshot
6435 .buffer_snapshot
6436 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6437 cx,
6438 );
6439 }
6440 }
6441
6442 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6443 let snapshot = self
6444 .display_map
6445 .update(cx, |display_map, cx| display_map.snapshot(cx));
6446 let selection = self.selections.newest::<Point>(cx);
6447
6448 if !self.seek_in_direction(
6449 &snapshot,
6450 selection.head(),
6451 false,
6452 snapshot
6453 .buffer_snapshot
6454 .git_diff_hunks_in_range_rev(0..selection.head().row),
6455 cx,
6456 ) {
6457 let wrapped_point = snapshot.buffer_snapshot.max_point();
6458 self.seek_in_direction(
6459 &snapshot,
6460 wrapped_point,
6461 true,
6462 snapshot
6463 .buffer_snapshot
6464 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6465 cx,
6466 );
6467 }
6468 }
6469
6470 fn seek_in_direction(
6471 &mut self,
6472 snapshot: &DisplaySnapshot,
6473 initial_point: Point,
6474 is_wrapped: bool,
6475 hunks: impl Iterator<Item = DiffHunk<u32>>,
6476 cx: &mut ViewContext<Editor>,
6477 ) -> bool {
6478 let display_point = initial_point.to_display_point(snapshot);
6479 let mut hunks = hunks
6480 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6481 .skip_while(|hunk| {
6482 if is_wrapped {
6483 false
6484 } else {
6485 hunk.contains_display_row(display_point.row())
6486 }
6487 })
6488 .dedup();
6489
6490 if let Some(hunk) = hunks.next() {
6491 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6492 let row = hunk.start_display_row();
6493 let point = DisplayPoint::new(row, 0);
6494 s.select_display_ranges([point..point]);
6495 });
6496
6497 true
6498 } else {
6499 false
6500 }
6501 }
6502
6503 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6504 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6505 }
6506
6507 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6508 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6509 }
6510
6511 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6512 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6513 }
6514
6515 pub fn go_to_type_definition_split(
6516 &mut self,
6517 _: &GoToTypeDefinitionSplit,
6518 cx: &mut ViewContext<Self>,
6519 ) {
6520 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6521 }
6522
6523 fn go_to_definition_of_kind(
6524 &mut self,
6525 kind: GotoDefinitionKind,
6526 split: bool,
6527 cx: &mut ViewContext<Self>,
6528 ) {
6529 let Some(workspace) = self.workspace(cx) else { return };
6530 let buffer = self.buffer.read(cx);
6531 let head = self.selections.newest::<usize>(cx).head();
6532 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6533 text_anchor
6534 } else {
6535 return;
6536 };
6537
6538 let project = workspace.read(cx).project().clone();
6539 let definitions = project.update(cx, |project, cx| match kind {
6540 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6541 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6542 });
6543
6544 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6545 let definitions = definitions.await?;
6546 editor.update(&mut cx, |editor, cx| {
6547 editor.navigate_to_definitions(definitions, split, cx);
6548 })?;
6549 Ok::<(), anyhow::Error>(())
6550 })
6551 .detach_and_log_err(cx);
6552 }
6553
6554 pub fn navigate_to_definitions(
6555 &mut self,
6556 mut definitions: Vec<LocationLink>,
6557 split: bool,
6558 cx: &mut ViewContext<Editor>,
6559 ) {
6560 let Some(workspace) = self.workspace(cx) else { return };
6561 let pane = workspace.read(cx).active_pane().clone();
6562 // If there is one definition, just open it directly
6563 if definitions.len() == 1 {
6564 let definition = definitions.pop().unwrap();
6565 let range = definition
6566 .target
6567 .range
6568 .to_offset(definition.target.buffer.read(cx));
6569
6570 let range = self.range_for_match(&range);
6571 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6572 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6573 s.select_ranges([range]);
6574 });
6575 } else {
6576 cx.window_context().defer(move |cx| {
6577 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6578 if split {
6579 workspace.split_project_item(definition.target.buffer.clone(), cx)
6580 } else {
6581 workspace.open_project_item(definition.target.buffer.clone(), cx)
6582 }
6583 });
6584 target_editor.update(cx, |target_editor, cx| {
6585 // When selecting a definition in a different buffer, disable the nav history
6586 // to avoid creating a history entry at the previous cursor location.
6587 pane.update(cx, |pane, _| pane.disable_history());
6588 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6589 s.select_ranges([range]);
6590 });
6591 pane.update(cx, |pane, _| pane.enable_history());
6592 });
6593 });
6594 }
6595 } else if !definitions.is_empty() {
6596 let replica_id = self.replica_id(cx);
6597 cx.window_context().defer(move |cx| {
6598 let title = definitions
6599 .iter()
6600 .find(|definition| definition.origin.is_some())
6601 .and_then(|definition| {
6602 definition.origin.as_ref().map(|origin| {
6603 let buffer = origin.buffer.read(cx);
6604 format!(
6605 "Definitions for {}",
6606 buffer
6607 .text_for_range(origin.range.clone())
6608 .collect::<String>()
6609 )
6610 })
6611 })
6612 .unwrap_or("Definitions".to_owned());
6613 let locations = definitions
6614 .into_iter()
6615 .map(|definition| definition.target)
6616 .collect();
6617 workspace.update(cx, |workspace, cx| {
6618 Self::open_locations_in_multibuffer(
6619 workspace, locations, replica_id, title, split, cx,
6620 )
6621 });
6622 });
6623 }
6624 }
6625
6626 pub fn find_all_references(
6627 workspace: &mut Workspace,
6628 _: &FindAllReferences,
6629 cx: &mut ViewContext<Workspace>,
6630 ) -> Option<Task<Result<()>>> {
6631 let active_item = workspace.active_item(cx)?;
6632 let editor_handle = active_item.act_as::<Self>(cx)?;
6633
6634 let editor = editor_handle.read(cx);
6635 let buffer = editor.buffer.read(cx);
6636 let head = editor.selections.newest::<usize>(cx).head();
6637 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6638 let replica_id = editor.replica_id(cx);
6639
6640 let project = workspace.project().clone();
6641 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6642 Some(cx.spawn_labeled(
6643 "Finding All References...",
6644 |workspace, mut cx| async move {
6645 let locations = references.await?;
6646 if locations.is_empty() {
6647 return Ok(());
6648 }
6649
6650 workspace.update(&mut cx, |workspace, cx| {
6651 let title = locations
6652 .first()
6653 .as_ref()
6654 .map(|location| {
6655 let buffer = location.buffer.read(cx);
6656 format!(
6657 "References to `{}`",
6658 buffer
6659 .text_for_range(location.range.clone())
6660 .collect::<String>()
6661 )
6662 })
6663 .unwrap();
6664 Self::open_locations_in_multibuffer(
6665 workspace, locations, replica_id, title, false, cx,
6666 );
6667 })?;
6668
6669 Ok(())
6670 },
6671 ))
6672 }
6673
6674 /// Opens a multibuffer with the given project locations in it
6675 pub fn open_locations_in_multibuffer(
6676 workspace: &mut Workspace,
6677 mut locations: Vec<Location>,
6678 replica_id: ReplicaId,
6679 title: String,
6680 split: bool,
6681 cx: &mut ViewContext<Workspace>,
6682 ) {
6683 // If there are multiple definitions, open them in a multibuffer
6684 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6685 let mut locations = locations.into_iter().peekable();
6686 let mut ranges_to_highlight = Vec::new();
6687
6688 let excerpt_buffer = cx.add_model(|cx| {
6689 let mut multibuffer = MultiBuffer::new(replica_id);
6690 while let Some(location) = locations.next() {
6691 let buffer = location.buffer.read(cx);
6692 let mut ranges_for_buffer = Vec::new();
6693 let range = location.range.to_offset(buffer);
6694 ranges_for_buffer.push(range.clone());
6695
6696 while let Some(next_location) = locations.peek() {
6697 if next_location.buffer == location.buffer {
6698 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6699 locations.next();
6700 } else {
6701 break;
6702 }
6703 }
6704
6705 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6706 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6707 location.buffer.clone(),
6708 ranges_for_buffer,
6709 1,
6710 cx,
6711 ))
6712 }
6713
6714 multibuffer.with_title(title)
6715 });
6716
6717 let editor = cx.add_view(|cx| {
6718 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6719 });
6720 editor.update(cx, |editor, cx| {
6721 editor.highlight_background::<Self>(
6722 ranges_to_highlight,
6723 |theme| theme.editor.highlighted_line_background,
6724 cx,
6725 );
6726 });
6727 if split {
6728 workspace.split_item(Box::new(editor), cx);
6729 } else {
6730 workspace.add_item(Box::new(editor), cx);
6731 }
6732 }
6733
6734 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6735 use language::ToOffset as _;
6736
6737 let project = self.project.clone()?;
6738 let selection = self.selections.newest_anchor().clone();
6739 let (cursor_buffer, cursor_buffer_position) = self
6740 .buffer
6741 .read(cx)
6742 .text_anchor_for_position(selection.head(), cx)?;
6743 let (tail_buffer, _) = self
6744 .buffer
6745 .read(cx)
6746 .text_anchor_for_position(selection.tail(), cx)?;
6747 if tail_buffer != cursor_buffer {
6748 return None;
6749 }
6750
6751 let snapshot = cursor_buffer.read(cx).snapshot();
6752 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6753 let prepare_rename = project.update(cx, |project, cx| {
6754 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6755 });
6756
6757 Some(cx.spawn(|this, mut cx| async move {
6758 let rename_range = if let Some(range) = prepare_rename.await? {
6759 Some(range)
6760 } else {
6761 this.update(&mut cx, |this, cx| {
6762 let buffer = this.buffer.read(cx).snapshot(cx);
6763 let display_snapshot = this
6764 .display_map
6765 .update(cx, |display_map, cx| display_map.snapshot(cx));
6766 let mut buffer_highlights = this
6767 .document_highlights_for_position(
6768 selection.head(),
6769 &buffer,
6770 &display_snapshot,
6771 )
6772 .filter_map(|highlight| highlight.as_text_range())
6773 .filter(|highlight| {
6774 highlight.start.excerpt_id() == selection.head().excerpt_id()
6775 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6776 });
6777 buffer_highlights
6778 .next()
6779 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6780 })?
6781 };
6782 if let Some(rename_range) = rename_range {
6783 let rename_buffer_range = rename_range.to_offset(&snapshot);
6784 let cursor_offset_in_rename_range =
6785 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6786
6787 this.update(&mut cx, |this, cx| {
6788 this.take_rename(false, cx);
6789 let style = this.style(cx);
6790 let buffer = this.buffer.read(cx).read(cx);
6791 let cursor_offset = selection.head().to_offset(&buffer);
6792 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6793 let rename_end = rename_start + rename_buffer_range.len();
6794 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6795 let mut old_highlight_id = None;
6796 let old_name: Arc<str> = buffer
6797 .chunks(rename_start..rename_end, true)
6798 .map(|chunk| {
6799 if old_highlight_id.is_none() {
6800 old_highlight_id = chunk.syntax_highlight_id;
6801 }
6802 chunk.text
6803 })
6804 .collect::<String>()
6805 .into();
6806
6807 drop(buffer);
6808
6809 // Position the selection in the rename editor so that it matches the current selection.
6810 this.show_local_selections = false;
6811 let rename_editor = cx.add_view(|cx| {
6812 let mut editor = Editor::single_line(None, cx);
6813 if let Some(old_highlight_id) = old_highlight_id {
6814 editor.override_text_style =
6815 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6816 }
6817 editor.buffer.update(cx, |buffer, cx| {
6818 buffer.edit([(0..0, old_name.clone())], None, cx)
6819 });
6820 editor.select_all(&SelectAll, cx);
6821 editor
6822 });
6823
6824 let ranges = this
6825 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6826 .into_iter()
6827 .flat_map(|(_, ranges)| {
6828 ranges.into_iter().filter_map(|range| range.as_text_range())
6829 })
6830 .chain(
6831 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6832 .into_iter()
6833 .flat_map(|(_, ranges)| {
6834 ranges.into_iter().filter_map(|range| range.as_text_range())
6835 }),
6836 )
6837 .collect();
6838
6839 this.highlight_text::<Rename>(
6840 ranges,
6841 HighlightStyle {
6842 fade_out: Some(style.rename_fade),
6843 ..Default::default()
6844 },
6845 cx,
6846 );
6847 cx.focus(&rename_editor);
6848 let block_id = this.insert_blocks(
6849 [BlockProperties {
6850 style: BlockStyle::Flex,
6851 position: range.start.clone(),
6852 height: 1,
6853 render: Arc::new({
6854 let editor = rename_editor.clone();
6855 move |cx: &mut BlockContext| {
6856 ChildView::new(&editor, cx)
6857 .contained()
6858 .with_padding_left(cx.anchor_x)
6859 .into_any()
6860 }
6861 }),
6862 disposition: BlockDisposition::Below,
6863 }],
6864 Some(Autoscroll::fit()),
6865 cx,
6866 )[0];
6867 this.pending_rename = Some(RenameState {
6868 range,
6869 old_name,
6870 editor: rename_editor,
6871 block_id,
6872 });
6873 })?;
6874 }
6875
6876 Ok(())
6877 }))
6878 }
6879
6880 pub fn confirm_rename(
6881 workspace: &mut Workspace,
6882 _: &ConfirmRename,
6883 cx: &mut ViewContext<Workspace>,
6884 ) -> Option<Task<Result<()>>> {
6885 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6886
6887 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6888 let rename = editor.take_rename(false, cx)?;
6889 let buffer = editor.buffer.read(cx);
6890 let (start_buffer, start) =
6891 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6892 let (end_buffer, end) =
6893 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6894 if start_buffer == end_buffer {
6895 let new_name = rename.editor.read(cx).text(cx);
6896 Some((start_buffer, start..end, rename.old_name, new_name))
6897 } else {
6898 None
6899 }
6900 })?;
6901
6902 let rename = workspace.project().clone().update(cx, |project, cx| {
6903 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6904 });
6905
6906 let editor = editor.downgrade();
6907 Some(cx.spawn(|workspace, mut cx| async move {
6908 let project_transaction = rename.await?;
6909 Self::open_project_transaction(
6910 &editor,
6911 workspace,
6912 project_transaction,
6913 format!("Rename: {} → {}", old_name, new_name),
6914 cx.clone(),
6915 )
6916 .await?;
6917
6918 editor.update(&mut cx, |editor, cx| {
6919 editor.refresh_document_highlights(cx);
6920 })?;
6921 Ok(())
6922 }))
6923 }
6924
6925 fn take_rename(
6926 &mut self,
6927 moving_cursor: bool,
6928 cx: &mut ViewContext<Self>,
6929 ) -> Option<RenameState> {
6930 let rename = self.pending_rename.take()?;
6931 self.remove_blocks(
6932 [rename.block_id].into_iter().collect(),
6933 Some(Autoscroll::fit()),
6934 cx,
6935 );
6936 self.clear_text_highlights::<Rename>(cx);
6937 self.show_local_selections = true;
6938
6939 if moving_cursor {
6940 let rename_editor = rename.editor.read(cx);
6941 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6942
6943 // Update the selection to match the position of the selection inside
6944 // the rename editor.
6945 let snapshot = self.buffer.read(cx).read(cx);
6946 let rename_range = rename.range.to_offset(&snapshot);
6947 let cursor_in_editor = snapshot
6948 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6949 .min(rename_range.end);
6950 drop(snapshot);
6951
6952 self.change_selections(None, cx, |s| {
6953 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6954 });
6955 } else {
6956 self.refresh_document_highlights(cx);
6957 }
6958
6959 Some(rename)
6960 }
6961
6962 #[cfg(any(test, feature = "test-support"))]
6963 pub fn pending_rename(&self) -> Option<&RenameState> {
6964 self.pending_rename.as_ref()
6965 }
6966
6967 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6968 let project = match &self.project {
6969 Some(project) => project.clone(),
6970 None => return None,
6971 };
6972
6973 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6974 }
6975
6976 fn perform_format(
6977 &mut self,
6978 project: ModelHandle<Project>,
6979 trigger: FormatTrigger,
6980 cx: &mut ViewContext<Self>,
6981 ) -> Task<Result<()>> {
6982 let buffer = self.buffer().clone();
6983 let buffers = buffer.read(cx).all_buffers();
6984
6985 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6986 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6987
6988 cx.spawn(|_, mut cx| async move {
6989 let transaction = futures::select_biased! {
6990 _ = timeout => {
6991 log::warn!("timed out waiting for formatting");
6992 None
6993 }
6994 transaction = format.log_err().fuse() => transaction,
6995 };
6996
6997 buffer.update(&mut cx, |buffer, cx| {
6998 if let Some(transaction) = transaction {
6999 if !buffer.is_singleton() {
7000 buffer.push_transaction(&transaction.0, cx);
7001 }
7002 }
7003
7004 cx.notify();
7005 });
7006
7007 Ok(())
7008 })
7009 }
7010
7011 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7012 if let Some(project) = self.project.clone() {
7013 self.buffer.update(cx, |multi_buffer, cx| {
7014 project.update(cx, |project, cx| {
7015 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7016 });
7017 })
7018 }
7019 }
7020
7021 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7022 cx.show_character_palette();
7023 }
7024
7025 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7026 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7027 let buffer = self.buffer.read(cx).snapshot(cx);
7028 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7029 let is_valid = buffer
7030 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7031 .any(|entry| {
7032 entry.diagnostic.is_primary
7033 && !entry.range.is_empty()
7034 && entry.range.start == primary_range_start
7035 && entry.diagnostic.message == active_diagnostics.primary_message
7036 });
7037
7038 if is_valid != active_diagnostics.is_valid {
7039 active_diagnostics.is_valid = is_valid;
7040 let mut new_styles = HashMap::default();
7041 for (block_id, diagnostic) in &active_diagnostics.blocks {
7042 new_styles.insert(
7043 *block_id,
7044 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7045 );
7046 }
7047 self.display_map
7048 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7049 }
7050 }
7051 }
7052
7053 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7054 self.dismiss_diagnostics(cx);
7055 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7056 let buffer = self.buffer.read(cx).snapshot(cx);
7057
7058 let mut primary_range = None;
7059 let mut primary_message = None;
7060 let mut group_end = Point::zero();
7061 let diagnostic_group = buffer
7062 .diagnostic_group::<Point>(group_id)
7063 .map(|entry| {
7064 if entry.range.end > group_end {
7065 group_end = entry.range.end;
7066 }
7067 if entry.diagnostic.is_primary {
7068 primary_range = Some(entry.range.clone());
7069 primary_message = Some(entry.diagnostic.message.clone());
7070 }
7071 entry
7072 })
7073 .collect::<Vec<_>>();
7074 let primary_range = primary_range?;
7075 let primary_message = primary_message?;
7076 let primary_range =
7077 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7078
7079 let blocks = display_map
7080 .insert_blocks(
7081 diagnostic_group.iter().map(|entry| {
7082 let diagnostic = entry.diagnostic.clone();
7083 let message_height = diagnostic.message.lines().count() as u8;
7084 BlockProperties {
7085 style: BlockStyle::Fixed,
7086 position: buffer.anchor_after(entry.range.start),
7087 height: message_height,
7088 render: diagnostic_block_renderer(diagnostic, true),
7089 disposition: BlockDisposition::Below,
7090 }
7091 }),
7092 cx,
7093 )
7094 .into_iter()
7095 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7096 .collect();
7097
7098 Some(ActiveDiagnosticGroup {
7099 primary_range,
7100 primary_message,
7101 blocks,
7102 is_valid: true,
7103 })
7104 });
7105 self.active_diagnostics.is_some()
7106 }
7107
7108 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7109 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7110 self.display_map.update(cx, |display_map, cx| {
7111 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7112 });
7113 cx.notify();
7114 }
7115 }
7116
7117 pub fn set_selections_from_remote(
7118 &mut self,
7119 selections: Vec<Selection<Anchor>>,
7120 pending_selection: Option<Selection<Anchor>>,
7121 cx: &mut ViewContext<Self>,
7122 ) {
7123 let old_cursor_position = self.selections.newest_anchor().head();
7124 self.selections.change_with(cx, |s| {
7125 s.select_anchors(selections);
7126 if let Some(pending_selection) = pending_selection {
7127 s.set_pending(pending_selection, SelectMode::Character);
7128 } else {
7129 s.clear_pending();
7130 }
7131 });
7132 self.selections_did_change(false, &old_cursor_position, cx);
7133 }
7134
7135 fn push_to_selection_history(&mut self) {
7136 self.selection_history.push(SelectionHistoryEntry {
7137 selections: self.selections.disjoint_anchors(),
7138 select_next_state: self.select_next_state.clone(),
7139 select_prev_state: self.select_prev_state.clone(),
7140 add_selections_state: self.add_selections_state.clone(),
7141 });
7142 }
7143
7144 pub fn transact(
7145 &mut self,
7146 cx: &mut ViewContext<Self>,
7147 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7148 ) -> Option<TransactionId> {
7149 self.start_transaction_at(Instant::now(), cx);
7150 update(self, cx);
7151 self.end_transaction_at(Instant::now(), cx)
7152 }
7153
7154 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7155 self.end_selection(cx);
7156 if let Some(tx_id) = self
7157 .buffer
7158 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7159 {
7160 self.selection_history
7161 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7162 }
7163 }
7164
7165 fn end_transaction_at(
7166 &mut self,
7167 now: Instant,
7168 cx: &mut ViewContext<Self>,
7169 ) -> Option<TransactionId> {
7170 if let Some(tx_id) = self
7171 .buffer
7172 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7173 {
7174 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7175 *end_selections = Some(self.selections.disjoint_anchors());
7176 } else {
7177 error!("unexpectedly ended a transaction that wasn't started by this editor");
7178 }
7179
7180 cx.emit(Event::Edited);
7181 Some(tx_id)
7182 } else {
7183 None
7184 }
7185 }
7186
7187 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7188 let mut fold_ranges = Vec::new();
7189
7190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7191
7192 let selections = self.selections.all::<Point>(cx);
7193 for selection in selections {
7194 let range = selection.range().sorted();
7195 let buffer_start_row = range.start.row;
7196
7197 for row in (0..=range.end.row).rev() {
7198 let fold_range = display_map.foldable_range(row);
7199
7200 if let Some(fold_range) = fold_range {
7201 if fold_range.end.row >= buffer_start_row {
7202 fold_ranges.push(fold_range);
7203 if row <= range.start.row {
7204 break;
7205 }
7206 }
7207 }
7208 }
7209 }
7210
7211 self.fold_ranges(fold_ranges, true, cx);
7212 }
7213
7214 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7215 let buffer_row = fold_at.buffer_row;
7216 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7217
7218 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7219 let autoscroll = self
7220 .selections
7221 .all::<Point>(cx)
7222 .iter()
7223 .any(|selection| fold_range.overlaps(&selection.range()));
7224
7225 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7226 }
7227 }
7228
7229 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7230 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7231 let buffer = &display_map.buffer_snapshot;
7232 let selections = self.selections.all::<Point>(cx);
7233 let ranges = selections
7234 .iter()
7235 .map(|s| {
7236 let range = s.display_range(&display_map).sorted();
7237 let mut start = range.start.to_point(&display_map);
7238 let mut end = range.end.to_point(&display_map);
7239 start.column = 0;
7240 end.column = buffer.line_len(end.row);
7241 start..end
7242 })
7243 .collect::<Vec<_>>();
7244
7245 self.unfold_ranges(ranges, true, true, cx);
7246 }
7247
7248 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7249 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7250
7251 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7252 ..Point::new(
7253 unfold_at.buffer_row,
7254 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7255 );
7256
7257 let autoscroll = self
7258 .selections
7259 .all::<Point>(cx)
7260 .iter()
7261 .any(|selection| selection.range().overlaps(&intersection_range));
7262
7263 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7264 }
7265
7266 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7267 let selections = self.selections.all::<Point>(cx);
7268 let ranges = selections.into_iter().map(|s| s.start..s.end);
7269 self.fold_ranges(ranges, true, cx);
7270 }
7271
7272 pub fn fold_ranges<T: ToOffset + Clone>(
7273 &mut self,
7274 ranges: impl IntoIterator<Item = Range<T>>,
7275 auto_scroll: bool,
7276 cx: &mut ViewContext<Self>,
7277 ) {
7278 let mut ranges = ranges.into_iter().peekable();
7279 if ranges.peek().is_some() {
7280 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7281
7282 if auto_scroll {
7283 self.request_autoscroll(Autoscroll::fit(), cx);
7284 }
7285
7286 cx.notify();
7287 }
7288 }
7289
7290 pub fn unfold_ranges<T: ToOffset + Clone>(
7291 &mut self,
7292 ranges: impl IntoIterator<Item = Range<T>>,
7293 inclusive: bool,
7294 auto_scroll: bool,
7295 cx: &mut ViewContext<Self>,
7296 ) {
7297 let mut ranges = ranges.into_iter().peekable();
7298 if ranges.peek().is_some() {
7299 self.display_map
7300 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7301 if auto_scroll {
7302 self.request_autoscroll(Autoscroll::fit(), cx);
7303 }
7304
7305 cx.notify();
7306 }
7307 }
7308
7309 pub fn gutter_hover(
7310 &mut self,
7311 GutterHover { hovered }: &GutterHover,
7312 cx: &mut ViewContext<Self>,
7313 ) {
7314 self.gutter_hovered = *hovered;
7315 cx.notify();
7316 }
7317
7318 pub fn insert_blocks(
7319 &mut self,
7320 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7321 autoscroll: Option<Autoscroll>,
7322 cx: &mut ViewContext<Self>,
7323 ) -> Vec<BlockId> {
7324 let blocks = self
7325 .display_map
7326 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7327 if let Some(autoscroll) = autoscroll {
7328 self.request_autoscroll(autoscroll, cx);
7329 }
7330 blocks
7331 }
7332
7333 pub fn replace_blocks(
7334 &mut self,
7335 blocks: HashMap<BlockId, RenderBlock>,
7336 autoscroll: Option<Autoscroll>,
7337 cx: &mut ViewContext<Self>,
7338 ) {
7339 self.display_map
7340 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7341 if let Some(autoscroll) = autoscroll {
7342 self.request_autoscroll(autoscroll, cx);
7343 }
7344 }
7345
7346 pub fn remove_blocks(
7347 &mut self,
7348 block_ids: HashSet<BlockId>,
7349 autoscroll: Option<Autoscroll>,
7350 cx: &mut ViewContext<Self>,
7351 ) {
7352 self.display_map.update(cx, |display_map, cx| {
7353 display_map.remove_blocks(block_ids, cx)
7354 });
7355 if let Some(autoscroll) = autoscroll {
7356 self.request_autoscroll(autoscroll, cx);
7357 }
7358 }
7359
7360 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7361 self.display_map
7362 .update(cx, |map, cx| map.snapshot(cx))
7363 .longest_row()
7364 }
7365
7366 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7367 self.display_map
7368 .update(cx, |map, cx| map.snapshot(cx))
7369 .max_point()
7370 }
7371
7372 pub fn text(&self, cx: &AppContext) -> String {
7373 self.buffer.read(cx).read(cx).text()
7374 }
7375
7376 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7377 self.transact(cx, |this, cx| {
7378 this.buffer
7379 .read(cx)
7380 .as_singleton()
7381 .expect("you can only call set_text on editors for singleton buffers")
7382 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7383 });
7384 }
7385
7386 pub fn display_text(&self, cx: &mut AppContext) -> String {
7387 self.display_map
7388 .update(cx, |map, cx| map.snapshot(cx))
7389 .text()
7390 }
7391
7392 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7393 let mut wrap_guides = smallvec::smallvec![];
7394
7395 if self.show_wrap_guides == Some(false) {
7396 return wrap_guides;
7397 }
7398
7399 let settings = self.buffer.read(cx).settings_at(0, cx);
7400 if settings.show_wrap_guides {
7401 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7402 wrap_guides.push((soft_wrap as usize, true));
7403 }
7404 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7405 }
7406
7407 wrap_guides
7408 }
7409
7410 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7411 let settings = self.buffer.read(cx).settings_at(0, cx);
7412 let mode = self
7413 .soft_wrap_mode_override
7414 .unwrap_or_else(|| settings.soft_wrap);
7415 match mode {
7416 language_settings::SoftWrap::None => SoftWrap::None,
7417 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7418 language_settings::SoftWrap::PreferredLineLength => {
7419 SoftWrap::Column(settings.preferred_line_length)
7420 }
7421 }
7422 }
7423
7424 pub fn set_soft_wrap_mode(
7425 &mut self,
7426 mode: language_settings::SoftWrap,
7427 cx: &mut ViewContext<Self>,
7428 ) {
7429 self.soft_wrap_mode_override = Some(mode);
7430 cx.notify();
7431 }
7432
7433 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7434 self.display_map
7435 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7436 }
7437
7438 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7439 if self.soft_wrap_mode_override.is_some() {
7440 self.soft_wrap_mode_override.take();
7441 } else {
7442 let soft_wrap = match self.soft_wrap_mode(cx) {
7443 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7444 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7445 };
7446 self.soft_wrap_mode_override = Some(soft_wrap);
7447 }
7448 cx.notify();
7449 }
7450
7451 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7452 self.show_gutter = show_gutter;
7453 cx.notify();
7454 }
7455
7456 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7457 self.show_wrap_guides = Some(show_gutter);
7458 cx.notify();
7459 }
7460
7461 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7462 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7463 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7464 cx.reveal_path(&file.abs_path(cx));
7465 }
7466 }
7467 }
7468
7469 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7470 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7471 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7472 if let Some(path) = file.abs_path(cx).to_str() {
7473 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7474 }
7475 }
7476 }
7477 }
7478
7479 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7480 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7481 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7482 if let Some(path) = file.path().to_str() {
7483 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7484 }
7485 }
7486 }
7487 }
7488
7489 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7490 self.highlighted_rows = rows;
7491 }
7492
7493 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7494 self.highlighted_rows.clone()
7495 }
7496
7497 pub fn highlight_background<T: 'static>(
7498 &mut self,
7499 ranges: Vec<Range<Anchor>>,
7500 color_fetcher: fn(&Theme) -> Color,
7501 cx: &mut ViewContext<Self>,
7502 ) {
7503 self.background_highlights.insert(
7504 TypeId::of::<T>(),
7505 (
7506 color_fetcher,
7507 ranges.into_iter().map(DocumentRange::Text).collect(),
7508 ),
7509 );
7510 cx.notify();
7511 }
7512
7513 pub fn highlight_inlay_background<T: 'static>(
7514 &mut self,
7515 ranges: Vec<InlayRange>,
7516 color_fetcher: fn(&Theme) -> Color,
7517 cx: &mut ViewContext<Self>,
7518 ) {
7519 self.background_highlights.insert(
7520 TypeId::of::<T>(),
7521 (
7522 color_fetcher,
7523 ranges.into_iter().map(DocumentRange::Inlay).collect(),
7524 ),
7525 );
7526 cx.notify();
7527 }
7528
7529 pub fn clear_background_highlights<T: 'static>(
7530 &mut self,
7531 cx: &mut ViewContext<Self>,
7532 ) -> Option<BackgroundHighlight> {
7533 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7534 if highlights.is_some() {
7535 cx.notify();
7536 }
7537 highlights
7538 }
7539
7540 #[cfg(feature = "test-support")]
7541 pub fn all_background_highlights(
7542 &mut self,
7543 cx: &mut ViewContext<Self>,
7544 ) -> Vec<(Range<DisplayPoint>, Color)> {
7545 let snapshot = self.snapshot(cx);
7546 let buffer = &snapshot.buffer_snapshot;
7547 let start = buffer.anchor_before(0);
7548 let end = buffer.anchor_after(buffer.len());
7549 let theme = theme::current(cx);
7550 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7551 }
7552
7553 fn document_highlights_for_position<'a>(
7554 &'a self,
7555 position: Anchor,
7556 buffer: &'a MultiBufferSnapshot,
7557 display_snapshot: &'a DisplaySnapshot,
7558 ) -> impl 'a + Iterator<Item = &DocumentRange> {
7559 let read_highlights = self
7560 .background_highlights
7561 .get(&TypeId::of::<DocumentHighlightRead>())
7562 .map(|h| &h.1);
7563 let write_highlights = self
7564 .background_highlights
7565 .get(&TypeId::of::<DocumentHighlightWrite>())
7566 .map(|h| &h.1);
7567 let left_position = display_snapshot.anchor_to_inlay_offset(position.bias_left(buffer));
7568 let right_position = display_snapshot.anchor_to_inlay_offset(position.bias_right(buffer));
7569 read_highlights
7570 .into_iter()
7571 .chain(write_highlights)
7572 .flat_map(move |ranges| {
7573 let start_ix = match ranges.binary_search_by(|probe| {
7574 let cmp = document_to_inlay_range(probe, display_snapshot)
7575 .end
7576 .cmp(&left_position);
7577 if cmp.is_ge() {
7578 Ordering::Greater
7579 } else {
7580 Ordering::Less
7581 }
7582 }) {
7583 Ok(i) | Err(i) => i,
7584 };
7585
7586 let right_position = right_position.clone();
7587 ranges[start_ix..].iter().take_while(move |range| {
7588 document_to_inlay_range(range, display_snapshot)
7589 .start
7590 .cmp(&right_position)
7591 .is_le()
7592 })
7593 })
7594 }
7595
7596 pub fn background_highlights_in_range(
7597 &self,
7598 search_range: Range<Anchor>,
7599 display_snapshot: &DisplaySnapshot,
7600 theme: &Theme,
7601 ) -> Vec<(Range<DisplayPoint>, Color)> {
7602 let search_range = display_snapshot.anchor_to_inlay_offset(search_range.start)
7603 ..display_snapshot.anchor_to_inlay_offset(search_range.end);
7604 let mut results = Vec::new();
7605 for (color_fetcher, ranges) in self.background_highlights.values() {
7606 let color = color_fetcher(theme);
7607 let start_ix = match ranges.binary_search_by(|probe| {
7608 let cmp = document_to_inlay_range(probe, display_snapshot)
7609 .end
7610 .cmp(&search_range.start);
7611 if cmp.is_gt() {
7612 Ordering::Greater
7613 } else {
7614 Ordering::Less
7615 }
7616 }) {
7617 Ok(i) | Err(i) => i,
7618 };
7619 for range in &ranges[start_ix..] {
7620 let range = document_to_inlay_range(range, display_snapshot);
7621 if range.start.cmp(&search_range.end).is_ge() {
7622 break;
7623 }
7624
7625 let start = display_snapshot.inlay_offset_to_display_point(range.start, Bias::Left);
7626 let end = display_snapshot.inlay_offset_to_display_point(range.end, Bias::Right);
7627 results.push((start..end, color))
7628 }
7629 }
7630 results
7631 }
7632
7633 pub fn background_highlight_row_ranges<T: 'static>(
7634 &self,
7635 search_range: Range<Anchor>,
7636 display_snapshot: &DisplaySnapshot,
7637 count: usize,
7638 ) -> Vec<RangeInclusive<DisplayPoint>> {
7639 let search_range = display_snapshot.anchor_to_inlay_offset(search_range.start)
7640 ..display_snapshot.anchor_to_inlay_offset(search_range.end);
7641 let mut results = Vec::new();
7642 let Some((_, ranges)) = self.background_highlights
7643 .get(&TypeId::of::<T>()) else {
7644 return vec![];
7645 };
7646
7647 let start_ix = match ranges.binary_search_by(|probe| {
7648 let cmp = document_to_inlay_range(probe, display_snapshot)
7649 .end
7650 .cmp(&search_range.start);
7651 if cmp.is_gt() {
7652 Ordering::Greater
7653 } else {
7654 Ordering::Less
7655 }
7656 }) {
7657 Ok(i) | Err(i) => i,
7658 };
7659 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7660 if let (Some(start_display), Some(end_display)) = (start, end) {
7661 results.push(
7662 start_display.to_display_point(display_snapshot)
7663 ..=end_display.to_display_point(display_snapshot),
7664 );
7665 }
7666 };
7667 let mut start_row: Option<Point> = None;
7668 let mut end_row: Option<Point> = None;
7669 if ranges.len() > count {
7670 return Vec::new();
7671 }
7672 for range in &ranges[start_ix..] {
7673 let range = document_to_inlay_range(range, display_snapshot);
7674 if range.start.cmp(&search_range.end).is_ge() {
7675 break;
7676 }
7677 let end = display_snapshot
7678 .inlay_offset_to_display_point(range.end, Bias::Right)
7679 .to_point(display_snapshot);
7680 if let Some(current_row) = &end_row {
7681 if end.row == current_row.row {
7682 continue;
7683 }
7684 }
7685 let start = display_snapshot
7686 .inlay_offset_to_display_point(range.start, Bias::Left)
7687 .to_point(display_snapshot);
7688
7689 if start_row.is_none() {
7690 assert_eq!(end_row, None);
7691 start_row = Some(start);
7692 end_row = Some(end);
7693 continue;
7694 }
7695 if let Some(current_end) = end_row.as_mut() {
7696 if start.row > current_end.row + 1 {
7697 push_region(start_row, end_row);
7698 start_row = Some(start);
7699 end_row = Some(end);
7700 } else {
7701 // Merge two hunks.
7702 *current_end = end;
7703 }
7704 } else {
7705 unreachable!();
7706 }
7707 }
7708 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7709 push_region(start_row, end_row);
7710 results
7711 }
7712
7713 pub fn highlight_text<T: 'static>(
7714 &mut self,
7715 ranges: Vec<Range<Anchor>>,
7716 style: HighlightStyle,
7717 cx: &mut ViewContext<Self>,
7718 ) {
7719 self.display_map.update(cx, |map, _| {
7720 map.highlight_text(TypeId::of::<T>(), ranges, style)
7721 });
7722 cx.notify();
7723 }
7724
7725 pub fn highlight_inlays<T: 'static>(
7726 &mut self,
7727 ranges: Vec<InlayRange>,
7728 style: HighlightStyle,
7729 cx: &mut ViewContext<Self>,
7730 ) {
7731 self.display_map.update(cx, |map, _| {
7732 map.highlight_inlays(TypeId::of::<T>(), ranges, style)
7733 });
7734 cx.notify();
7735 }
7736
7737 pub fn text_highlights<'a, T: 'static>(
7738 &'a self,
7739 cx: &'a AppContext,
7740 ) -> Option<(HighlightStyle, &'a [DocumentRange])> {
7741 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7742 }
7743
7744 pub fn clear_text_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
7745 let text_highlights = self
7746 .display_map
7747 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7748 if text_highlights.is_some() {
7749 cx.notify();
7750 }
7751 }
7752
7753 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7754 self.blink_manager.read(cx).visible() && self.focused
7755 }
7756
7757 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7758 cx.notify();
7759 }
7760
7761 fn on_buffer_event(
7762 &mut self,
7763 multibuffer: ModelHandle<MultiBuffer>,
7764 event: &multi_buffer::Event,
7765 cx: &mut ViewContext<Self>,
7766 ) {
7767 match event {
7768 multi_buffer::Event::Edited => {
7769 self.refresh_active_diagnostics(cx);
7770 self.refresh_code_actions(cx);
7771 if self.has_active_copilot_suggestion(cx) {
7772 self.update_visible_copilot_suggestion(cx);
7773 }
7774 cx.emit(Event::BufferEdited);
7775
7776 if let Some(project) = &self.project {
7777 let project = project.read(cx);
7778 let languages_affected = multibuffer
7779 .read(cx)
7780 .all_buffers()
7781 .into_iter()
7782 .filter_map(|buffer| {
7783 let buffer = buffer.read(cx);
7784 let language = buffer.language()?;
7785 if project.is_local()
7786 && project.language_servers_for_buffer(buffer, cx).count() == 0
7787 {
7788 None
7789 } else {
7790 Some(language)
7791 }
7792 })
7793 .cloned()
7794 .collect::<HashSet<_>>();
7795 if !languages_affected.is_empty() {
7796 self.refresh_inlay_hints(
7797 InlayHintRefreshReason::BufferEdited(languages_affected),
7798 cx,
7799 );
7800 }
7801 }
7802 }
7803 multi_buffer::Event::ExcerptsAdded {
7804 buffer,
7805 predecessor,
7806 excerpts,
7807 } => cx.emit(Event::ExcerptsAdded {
7808 buffer: buffer.clone(),
7809 predecessor: *predecessor,
7810 excerpts: excerpts.clone(),
7811 }),
7812 multi_buffer::Event::ExcerptsRemoved { ids } => {
7813 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7814 }
7815 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7816 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7817 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7818 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7819 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7820 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7821 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7822 multi_buffer::Event::DiagnosticsUpdated => {
7823 self.refresh_active_diagnostics(cx);
7824 }
7825 _ => {}
7826 };
7827 }
7828
7829 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7830 cx.notify();
7831 }
7832
7833 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7834 self.refresh_copilot_suggestions(true, cx);
7835 self.refresh_inlay_hints(
7836 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
7837 self.selections.newest_anchor().head(),
7838 &self.buffer.read(cx).snapshot(cx),
7839 cx,
7840 )),
7841 cx,
7842 );
7843 }
7844
7845 pub fn set_searchable(&mut self, searchable: bool) {
7846 self.searchable = searchable;
7847 }
7848
7849 pub fn searchable(&self) -> bool {
7850 self.searchable
7851 }
7852
7853 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7854 let active_item = workspace.active_item(cx);
7855 let editor_handle = if let Some(editor) = active_item
7856 .as_ref()
7857 .and_then(|item| item.act_as::<Self>(cx))
7858 {
7859 editor
7860 } else {
7861 cx.propagate_action();
7862 return;
7863 };
7864
7865 let editor = editor_handle.read(cx);
7866 let buffer = editor.buffer.read(cx);
7867 if buffer.is_singleton() {
7868 cx.propagate_action();
7869 return;
7870 }
7871
7872 let mut new_selections_by_buffer = HashMap::default();
7873 for selection in editor.selections.all::<usize>(cx) {
7874 for (buffer, mut range, _) in
7875 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7876 {
7877 if selection.reversed {
7878 mem::swap(&mut range.start, &mut range.end);
7879 }
7880 new_selections_by_buffer
7881 .entry(buffer)
7882 .or_insert(Vec::new())
7883 .push(range)
7884 }
7885 }
7886
7887 editor_handle.update(cx, |editor, cx| {
7888 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7889 });
7890 let pane = workspace.active_pane().clone();
7891 pane.update(cx, |pane, _| pane.disable_history());
7892
7893 // We defer the pane interaction because we ourselves are a workspace item
7894 // and activating a new item causes the pane to call a method on us reentrantly,
7895 // which panics if we're on the stack.
7896 cx.defer(move |workspace, cx| {
7897 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7898 let editor = workspace.open_project_item::<Self>(buffer, cx);
7899 editor.update(cx, |editor, cx| {
7900 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7901 s.select_ranges(ranges);
7902 });
7903 });
7904 }
7905
7906 pane.update(cx, |pane, _| pane.enable_history());
7907 });
7908 }
7909
7910 fn jump(
7911 workspace: &mut Workspace,
7912 path: ProjectPath,
7913 position: Point,
7914 anchor: language::Anchor,
7915 cx: &mut ViewContext<Workspace>,
7916 ) {
7917 let editor = workspace.open_path(path, None, true, cx);
7918 cx.spawn(|_, mut cx| async move {
7919 let editor = editor
7920 .await?
7921 .downcast::<Editor>()
7922 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7923 .downgrade();
7924 editor.update(&mut cx, |editor, cx| {
7925 let buffer = editor
7926 .buffer()
7927 .read(cx)
7928 .as_singleton()
7929 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7930 let buffer = buffer.read(cx);
7931 let cursor = if buffer.can_resolve(&anchor) {
7932 language::ToPoint::to_point(&anchor, buffer)
7933 } else {
7934 buffer.clip_point(position, Bias::Left)
7935 };
7936
7937 let nav_history = editor.nav_history.take();
7938 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7939 s.select_ranges([cursor..cursor]);
7940 });
7941 editor.nav_history = nav_history;
7942
7943 anyhow::Ok(())
7944 })??;
7945
7946 anyhow::Ok(())
7947 })
7948 .detach_and_log_err(cx);
7949 }
7950
7951 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7952 let snapshot = self.buffer.read(cx).read(cx);
7953 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7954 Some(
7955 ranges
7956 .iter()
7957 .filter_map(|range| range.as_text_range())
7958 .map(move |range| {
7959 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7960 })
7961 .collect(),
7962 )
7963 }
7964
7965 fn selection_replacement_ranges(
7966 &self,
7967 range: Range<OffsetUtf16>,
7968 cx: &AppContext,
7969 ) -> Vec<Range<OffsetUtf16>> {
7970 let selections = self.selections.all::<OffsetUtf16>(cx);
7971 let newest_selection = selections
7972 .iter()
7973 .max_by_key(|selection| selection.id)
7974 .unwrap();
7975 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7976 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7977 let snapshot = self.buffer.read(cx).read(cx);
7978 selections
7979 .into_iter()
7980 .map(|mut selection| {
7981 selection.start.0 =
7982 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7983 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7984 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7985 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7986 })
7987 .collect()
7988 }
7989
7990 fn report_copilot_event(
7991 &self,
7992 suggestion_id: Option<String>,
7993 suggestion_accepted: bool,
7994 cx: &AppContext,
7995 ) {
7996 let Some(project) = &self.project else {
7997 return
7998 };
7999
8000 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8001 let file_extension = self
8002 .buffer
8003 .read(cx)
8004 .as_singleton()
8005 .and_then(|b| b.read(cx).file())
8006 .and_then(|file| Path::new(file.file_name(cx)).extension())
8007 .and_then(|e| e.to_str())
8008 .map(|a| a.to_string());
8009
8010 let telemetry = project.read(cx).client().telemetry().clone();
8011 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8012
8013 let event = ClickhouseEvent::Copilot {
8014 suggestion_id,
8015 suggestion_accepted,
8016 file_extension,
8017 };
8018 telemetry.report_clickhouse_event(event, telemetry_settings);
8019 }
8020
8021 fn report_editor_event(
8022 &self,
8023 operation: &'static str,
8024 file_extension: Option<String>,
8025 cx: &AppContext,
8026 ) {
8027 let Some(project) = &self.project else {
8028 return
8029 };
8030
8031 // If None, we are in a file without an extension
8032 let file = self
8033 .buffer
8034 .read(cx)
8035 .as_singleton()
8036 .and_then(|b| b.read(cx).file());
8037 let file_extension = file_extension.or(file
8038 .as_ref()
8039 .and_then(|file| Path::new(file.file_name(cx)).extension())
8040 .and_then(|e| e.to_str())
8041 .map(|a| a.to_string()));
8042
8043 let vim_mode = cx
8044 .global::<SettingsStore>()
8045 .raw_user_settings()
8046 .get("vim_mode")
8047 == Some(&serde_json::Value::Bool(true));
8048 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8049 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8050 let copilot_enabled_for_language = self
8051 .buffer
8052 .read(cx)
8053 .settings_at(0, cx)
8054 .show_copilot_suggestions;
8055
8056 let telemetry = project.read(cx).client().telemetry().clone();
8057 let event = ClickhouseEvent::Editor {
8058 file_extension,
8059 vim_mode,
8060 operation,
8061 copilot_enabled,
8062 copilot_enabled_for_language,
8063 };
8064 telemetry.report_clickhouse_event(event, telemetry_settings)
8065 }
8066
8067 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8068 /// with each line being an array of {text, highlight} objects.
8069 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8070 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8071 return;
8072 };
8073
8074 #[derive(Serialize)]
8075 struct Chunk<'a> {
8076 text: String,
8077 highlight: Option<&'a str>,
8078 }
8079
8080 let snapshot = buffer.read(cx).snapshot();
8081 let range = self
8082 .selected_text_range(cx)
8083 .and_then(|selected_range| {
8084 if selected_range.is_empty() {
8085 None
8086 } else {
8087 Some(selected_range)
8088 }
8089 })
8090 .unwrap_or_else(|| 0..snapshot.len());
8091
8092 let chunks = snapshot.chunks(range, true);
8093 let mut lines = Vec::new();
8094 let mut line: VecDeque<Chunk> = VecDeque::new();
8095
8096 let theme = &theme::current(cx).editor.syntax;
8097
8098 for chunk in chunks {
8099 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8100 let mut chunk_lines = chunk.text.split("\n").peekable();
8101 while let Some(text) = chunk_lines.next() {
8102 let mut merged_with_last_token = false;
8103 if let Some(last_token) = line.back_mut() {
8104 if last_token.highlight == highlight {
8105 last_token.text.push_str(text);
8106 merged_with_last_token = true;
8107 }
8108 }
8109
8110 if !merged_with_last_token {
8111 line.push_back(Chunk {
8112 text: text.into(),
8113 highlight,
8114 });
8115 }
8116
8117 if chunk_lines.peek().is_some() {
8118 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8119 line.pop_front();
8120 }
8121 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8122 line.pop_back();
8123 }
8124
8125 lines.push(mem::take(&mut line));
8126 }
8127 }
8128 }
8129
8130 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8131 cx.write_to_clipboard(ClipboardItem::new(lines));
8132 }
8133
8134 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8135 &self.inlay_hint_cache
8136 }
8137}
8138
8139fn document_to_inlay_range(
8140 range: &DocumentRange,
8141 snapshot: &DisplaySnapshot,
8142) -> Range<InlayOffset> {
8143 match range {
8144 DocumentRange::Text(text_range) => {
8145 snapshot.anchor_to_inlay_offset(text_range.start)
8146 ..snapshot.anchor_to_inlay_offset(text_range.end)
8147 }
8148 DocumentRange::Inlay(inlay_range) => inlay_range.highlight_start..inlay_range.highlight_end,
8149 }
8150}
8151
8152fn inlay_hint_settings(
8153 location: Anchor,
8154 snapshot: &MultiBufferSnapshot,
8155 cx: &mut ViewContext<'_, '_, Editor>,
8156) -> InlayHintSettings {
8157 let file = snapshot.file_at(location);
8158 let language = snapshot.language_at(location);
8159 let settings = all_language_settings(file, cx);
8160 settings
8161 .language(language.map(|l| l.name()).as_deref())
8162 .inlay_hints
8163}
8164
8165fn consume_contiguous_rows(
8166 contiguous_row_selections: &mut Vec<Selection<Point>>,
8167 selection: &Selection<Point>,
8168 display_map: &DisplaySnapshot,
8169 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8170) -> (u32, u32) {
8171 contiguous_row_selections.push(selection.clone());
8172 let start_row = selection.start.row;
8173 let mut end_row = ending_row(selection, display_map);
8174
8175 while let Some(next_selection) = selections.peek() {
8176 if next_selection.start.row <= end_row {
8177 end_row = ending_row(next_selection, display_map);
8178 contiguous_row_selections.push(selections.next().unwrap().clone());
8179 } else {
8180 break;
8181 }
8182 }
8183 (start_row, end_row)
8184}
8185
8186fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8187 if next_selection.end.column > 0 || next_selection.is_empty() {
8188 display_map.next_line_boundary(next_selection.end).0.row + 1
8189 } else {
8190 next_selection.end.row
8191 }
8192}
8193
8194impl EditorSnapshot {
8195 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8196 self.display_snapshot.buffer_snapshot.language_at(position)
8197 }
8198
8199 pub fn is_focused(&self) -> bool {
8200 self.is_focused
8201 }
8202
8203 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8204 self.placeholder_text.as_ref()
8205 }
8206
8207 pub fn scroll_position(&self) -> Vector2F {
8208 self.scroll_anchor.scroll_position(&self.display_snapshot)
8209 }
8210}
8211
8212impl Deref for EditorSnapshot {
8213 type Target = DisplaySnapshot;
8214
8215 fn deref(&self) -> &Self::Target {
8216 &self.display_snapshot
8217 }
8218}
8219
8220#[derive(Clone, Debug, PartialEq, Eq)]
8221pub enum Event {
8222 InputIgnored {
8223 text: Arc<str>,
8224 },
8225 ExcerptsAdded {
8226 buffer: ModelHandle<Buffer>,
8227 predecessor: ExcerptId,
8228 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8229 },
8230 ExcerptsRemoved {
8231 ids: Vec<ExcerptId>,
8232 },
8233 BufferEdited,
8234 Edited,
8235 Reparsed,
8236 Focused,
8237 Blurred,
8238 DirtyChanged,
8239 Saved,
8240 TitleChanged,
8241 DiffBaseChanged,
8242 SelectionsChanged {
8243 local: bool,
8244 },
8245 ScrollPositionChanged {
8246 local: bool,
8247 autoscroll: bool,
8248 },
8249 Closed,
8250}
8251
8252pub struct EditorFocused(pub ViewHandle<Editor>);
8253pub struct EditorBlurred(pub ViewHandle<Editor>);
8254pub struct EditorReleased(pub WeakViewHandle<Editor>);
8255
8256impl Entity for Editor {
8257 type Event = Event;
8258
8259 fn release(&mut self, cx: &mut AppContext) {
8260 cx.emit_global(EditorReleased(self.handle.clone()));
8261 }
8262}
8263
8264impl View for Editor {
8265 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8266 let style = self.style(cx);
8267 let font_changed = self.display_map.update(cx, |map, cx| {
8268 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8269 map.set_font(style.text.font_id, style.text.font_size, cx)
8270 });
8271
8272 if font_changed {
8273 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8274 hide_hover(editor, cx);
8275 hide_link_definition(editor, cx);
8276 });
8277 }
8278
8279 Stack::new()
8280 .with_child(EditorElement::new(style.clone()))
8281 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8282 .into_any()
8283 }
8284
8285 fn ui_name() -> &'static str {
8286 "Editor"
8287 }
8288
8289 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8290 if cx.is_self_focused() {
8291 let focused_event = EditorFocused(cx.handle());
8292 cx.emit(Event::Focused);
8293 cx.emit_global(focused_event);
8294 }
8295 if let Some(rename) = self.pending_rename.as_ref() {
8296 cx.focus(&rename.editor);
8297 } else {
8298 if !self.focused {
8299 self.blink_manager.update(cx, BlinkManager::enable);
8300 }
8301 self.focused = true;
8302 self.buffer.update(cx, |buffer, cx| {
8303 buffer.finalize_last_transaction(cx);
8304 if self.leader_replica_id.is_none() {
8305 buffer.set_active_selections(
8306 &self.selections.disjoint_anchors(),
8307 self.selections.line_mode,
8308 self.cursor_shape,
8309 cx,
8310 );
8311 }
8312 });
8313 }
8314 }
8315
8316 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8317 let blurred_event = EditorBlurred(cx.handle());
8318 cx.emit_global(blurred_event);
8319 self.focused = false;
8320 self.blink_manager.update(cx, BlinkManager::disable);
8321 self.buffer
8322 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8323 self.hide_context_menu(cx);
8324 hide_hover(self, cx);
8325 cx.emit(Event::Blurred);
8326 cx.notify();
8327 }
8328
8329 fn modifiers_changed(
8330 &mut self,
8331 event: &gpui::platform::ModifiersChangedEvent,
8332 cx: &mut ViewContext<Self>,
8333 ) -> bool {
8334 let pending_selection = self.has_pending_selection();
8335
8336 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8337 if event.cmd && !pending_selection {
8338 let point = point.clone();
8339 let snapshot = self.snapshot(cx);
8340 let kind = point.definition_kind(event.shift);
8341
8342 show_link_definition(kind, self, point, snapshot, cx);
8343 return false;
8344 }
8345 }
8346
8347 {
8348 if self.link_go_to_definition_state.symbol_range.is_some()
8349 || !self.link_go_to_definition_state.definitions.is_empty()
8350 {
8351 self.link_go_to_definition_state.symbol_range.take();
8352 self.link_go_to_definition_state.definitions.clear();
8353 cx.notify();
8354 }
8355
8356 self.link_go_to_definition_state.task = None;
8357
8358 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8359 }
8360
8361 false
8362 }
8363
8364 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8365 Self::reset_to_default_keymap_context(keymap);
8366 let mode = match self.mode {
8367 EditorMode::SingleLine => "single_line",
8368 EditorMode::AutoHeight { .. } => "auto_height",
8369 EditorMode::Full => "full",
8370 };
8371 keymap.add_key("mode", mode);
8372 if self.pending_rename.is_some() {
8373 keymap.add_identifier("renaming");
8374 }
8375 match self.context_menu.as_ref() {
8376 Some(ContextMenu::Completions(_)) => {
8377 keymap.add_identifier("menu");
8378 keymap.add_identifier("showing_completions")
8379 }
8380 Some(ContextMenu::CodeActions(_)) => {
8381 keymap.add_identifier("menu");
8382 keymap.add_identifier("showing_code_actions")
8383 }
8384 None => {}
8385 }
8386 for layer in self.keymap_context_layers.values() {
8387 keymap.extend(layer);
8388 }
8389
8390 if let Some(extension) = self
8391 .buffer
8392 .read(cx)
8393 .as_singleton()
8394 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8395 {
8396 keymap.add_key("extension", extension.to_string());
8397 }
8398 }
8399
8400 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8401 Some(
8402 self.buffer
8403 .read(cx)
8404 .read(cx)
8405 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8406 .collect(),
8407 )
8408 }
8409
8410 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8411 // Prevent the IME menu from appearing when holding down an alphabetic key
8412 // while input is disabled.
8413 if !self.input_enabled {
8414 return None;
8415 }
8416
8417 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8418 Some(range.start.0..range.end.0)
8419 }
8420
8421 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8422 let snapshot = self.buffer.read(cx).read(cx);
8423 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8424 let range = range.as_text_range()?;
8425 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8426 }
8427
8428 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8429 self.clear_text_highlights::<InputComposition>(cx);
8430 self.ime_transaction.take();
8431 }
8432
8433 fn replace_text_in_range(
8434 &mut self,
8435 range_utf16: Option<Range<usize>>,
8436 text: &str,
8437 cx: &mut ViewContext<Self>,
8438 ) {
8439 self.transact(cx, |this, cx| {
8440 if this.input_enabled {
8441 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8442 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8443 Some(this.selection_replacement_ranges(range_utf16, cx))
8444 } else {
8445 this.marked_text_ranges(cx)
8446 };
8447
8448 if let Some(new_selected_ranges) = new_selected_ranges {
8449 this.change_selections(None, cx, |selections| {
8450 selections.select_ranges(new_selected_ranges)
8451 });
8452 }
8453 }
8454
8455 this.handle_input(text, cx);
8456 });
8457
8458 if !self.input_enabled {
8459 return;
8460 }
8461
8462 if let Some(transaction) = self.ime_transaction {
8463 self.buffer.update(cx, |buffer, cx| {
8464 buffer.group_until_transaction(transaction, cx);
8465 });
8466 }
8467
8468 self.unmark_text(cx);
8469 }
8470
8471 fn replace_and_mark_text_in_range(
8472 &mut self,
8473 range_utf16: Option<Range<usize>>,
8474 text: &str,
8475 new_selected_range_utf16: Option<Range<usize>>,
8476 cx: &mut ViewContext<Self>,
8477 ) {
8478 if !self.input_enabled {
8479 return;
8480 }
8481
8482 let transaction = self.transact(cx, |this, cx| {
8483 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8484 let snapshot = this.buffer.read(cx).read(cx);
8485 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8486 for marked_range in &mut marked_ranges {
8487 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8488 marked_range.start.0 += relative_range_utf16.start;
8489 marked_range.start =
8490 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8491 marked_range.end =
8492 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8493 }
8494 }
8495 Some(marked_ranges)
8496 } else if let Some(range_utf16) = range_utf16 {
8497 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8498 Some(this.selection_replacement_ranges(range_utf16, cx))
8499 } else {
8500 None
8501 };
8502
8503 if let Some(ranges) = ranges_to_replace {
8504 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8505 }
8506
8507 let marked_ranges = {
8508 let snapshot = this.buffer.read(cx).read(cx);
8509 this.selections
8510 .disjoint_anchors()
8511 .iter()
8512 .map(|selection| {
8513 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8514 })
8515 .collect::<Vec<_>>()
8516 };
8517
8518 if text.is_empty() {
8519 this.unmark_text(cx);
8520 } else {
8521 this.highlight_text::<InputComposition>(
8522 marked_ranges.clone(),
8523 this.style(cx).composition_mark,
8524 cx,
8525 );
8526 }
8527
8528 this.handle_input(text, cx);
8529
8530 if let Some(new_selected_range) = new_selected_range_utf16 {
8531 let snapshot = this.buffer.read(cx).read(cx);
8532 let new_selected_ranges = marked_ranges
8533 .into_iter()
8534 .map(|marked_range| {
8535 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8536 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8537 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8538 snapshot.clip_offset_utf16(new_start, Bias::Left)
8539 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8540 })
8541 .collect::<Vec<_>>();
8542
8543 drop(snapshot);
8544 this.change_selections(None, cx, |selections| {
8545 selections.select_ranges(new_selected_ranges)
8546 });
8547 }
8548 });
8549
8550 self.ime_transaction = self.ime_transaction.or(transaction);
8551 if let Some(transaction) = self.ime_transaction {
8552 self.buffer.update(cx, |buffer, cx| {
8553 buffer.group_until_transaction(transaction, cx);
8554 });
8555 }
8556
8557 if self.text_highlights::<InputComposition>(cx).is_none() {
8558 self.ime_transaction.take();
8559 }
8560 }
8561}
8562
8563fn build_style(
8564 settings: &ThemeSettings,
8565 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8566 override_text_style: Option<&OverrideTextStyle>,
8567 cx: &AppContext,
8568) -> EditorStyle {
8569 let font_cache = cx.font_cache();
8570 let line_height_scalar = settings.line_height();
8571 let theme_id = settings.theme.meta.id;
8572 let mut theme = settings.theme.editor.clone();
8573 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8574 let field_editor_theme = get_field_editor_theme(&settings.theme);
8575 theme.text_color = field_editor_theme.text.color;
8576 theme.selection = field_editor_theme.selection;
8577 theme.background = field_editor_theme
8578 .container
8579 .background_color
8580 .unwrap_or_default();
8581 EditorStyle {
8582 text: field_editor_theme.text,
8583 placeholder_text: field_editor_theme.placeholder_text,
8584 line_height_scalar,
8585 theme,
8586 theme_id,
8587 }
8588 } else {
8589 let font_family_id = settings.buffer_font_family;
8590 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8591 let font_properties = Default::default();
8592 let font_id = font_cache
8593 .select_font(font_family_id, &font_properties)
8594 .unwrap();
8595 let font_size = settings.buffer_font_size(cx);
8596 EditorStyle {
8597 text: TextStyle {
8598 color: settings.theme.editor.text_color,
8599 font_family_name,
8600 font_family_id,
8601 font_id,
8602 font_size,
8603 font_properties,
8604 underline: Default::default(),
8605 soft_wrap: false,
8606 },
8607 placeholder_text: None,
8608 line_height_scalar,
8609 theme,
8610 theme_id,
8611 }
8612 };
8613
8614 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8615 if let Some(highlighted) = style
8616 .text
8617 .clone()
8618 .highlight(highlight_style, font_cache)
8619 .log_err()
8620 {
8621 style.text = highlighted;
8622 }
8623 }
8624
8625 style
8626}
8627
8628trait SelectionExt {
8629 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8630 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8631 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8632 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8633 -> Range<u32>;
8634}
8635
8636impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8637 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8638 let start = self.start.to_point(buffer);
8639 let end = self.end.to_point(buffer);
8640 if self.reversed {
8641 end..start
8642 } else {
8643 start..end
8644 }
8645 }
8646
8647 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8648 let start = self.start.to_offset(buffer);
8649 let end = self.end.to_offset(buffer);
8650 if self.reversed {
8651 end..start
8652 } else {
8653 start..end
8654 }
8655 }
8656
8657 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8658 let start = self
8659 .start
8660 .to_point(&map.buffer_snapshot)
8661 .to_display_point(map);
8662 let end = self
8663 .end
8664 .to_point(&map.buffer_snapshot)
8665 .to_display_point(map);
8666 if self.reversed {
8667 end..start
8668 } else {
8669 start..end
8670 }
8671 }
8672
8673 fn spanned_rows(
8674 &self,
8675 include_end_if_at_line_start: bool,
8676 map: &DisplaySnapshot,
8677 ) -> Range<u32> {
8678 let start = self.start.to_point(&map.buffer_snapshot);
8679 let mut end = self.end.to_point(&map.buffer_snapshot);
8680 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8681 end.row -= 1;
8682 }
8683
8684 let buffer_start = map.prev_line_boundary(start).0;
8685 let buffer_end = map.next_line_boundary(end).0;
8686 buffer_start.row..buffer_end.row + 1
8687 }
8688}
8689
8690impl<T: InvalidationRegion> InvalidationStack<T> {
8691 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8692 where
8693 S: Clone + ToOffset,
8694 {
8695 while let Some(region) = self.last() {
8696 let all_selections_inside_invalidation_ranges =
8697 if selections.len() == region.ranges().len() {
8698 selections
8699 .iter()
8700 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8701 .all(|(selection, invalidation_range)| {
8702 let head = selection.head().to_offset(buffer);
8703 invalidation_range.start <= head && invalidation_range.end >= head
8704 })
8705 } else {
8706 false
8707 };
8708
8709 if all_selections_inside_invalidation_ranges {
8710 break;
8711 } else {
8712 self.pop();
8713 }
8714 }
8715 }
8716}
8717
8718impl<T> Default for InvalidationStack<T> {
8719 fn default() -> Self {
8720 Self(Default::default())
8721 }
8722}
8723
8724impl<T> Deref for InvalidationStack<T> {
8725 type Target = Vec<T>;
8726
8727 fn deref(&self) -> &Self::Target {
8728 &self.0
8729 }
8730}
8731
8732impl<T> DerefMut for InvalidationStack<T> {
8733 fn deref_mut(&mut self) -> &mut Self::Target {
8734 &mut self.0
8735 }
8736}
8737
8738impl InvalidationRegion for SnippetState {
8739 fn ranges(&self) -> &[Range<Anchor>] {
8740 &self.ranges[self.active_index]
8741 }
8742}
8743
8744impl Deref for EditorStyle {
8745 type Target = theme::Editor;
8746
8747 fn deref(&self) -> &Self::Target {
8748 &self.theme
8749 }
8750}
8751
8752pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8753 let mut highlighted_lines = Vec::new();
8754
8755 for (index, line) in diagnostic.message.lines().enumerate() {
8756 let line = match &diagnostic.source {
8757 Some(source) if index == 0 => {
8758 let source_highlight = Vec::from_iter(0..source.len());
8759 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8760 }
8761
8762 _ => highlight_diagnostic_message(Vec::new(), line),
8763 };
8764 highlighted_lines.push(line);
8765 }
8766 let message = diagnostic.message;
8767 Arc::new(move |cx: &mut BlockContext| {
8768 let message = message.clone();
8769 let settings = settings::get::<ThemeSettings>(cx);
8770 let tooltip_style = settings.theme.tooltip.clone();
8771 let theme = &settings.theme.editor;
8772 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8773 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8774 let anchor_x = cx.anchor_x;
8775 enum BlockContextToolip {}
8776 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8777 Flex::column()
8778 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8779 Label::new(
8780 line.clone(),
8781 style.message.clone().with_font_size(font_size),
8782 )
8783 .with_highlights(highlights.clone())
8784 .contained()
8785 .with_margin_left(anchor_x)
8786 }))
8787 .aligned()
8788 .left()
8789 .into_any()
8790 })
8791 .with_cursor_style(CursorStyle::PointingHand)
8792 .on_click(MouseButton::Left, move |_, _, cx| {
8793 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8794 })
8795 // We really need to rethink this ID system...
8796 .with_tooltip::<BlockContextToolip>(
8797 cx.block_id,
8798 "Copy diagnostic message",
8799 None,
8800 tooltip_style,
8801 cx,
8802 )
8803 .into_any()
8804 })
8805}
8806
8807pub fn highlight_diagnostic_message(
8808 initial_highlights: Vec<usize>,
8809 message: &str,
8810) -> (String, Vec<usize>) {
8811 let mut message_without_backticks = String::new();
8812 let mut prev_offset = 0;
8813 let mut inside_block = false;
8814 let mut highlights = initial_highlights;
8815 for (match_ix, (offset, _)) in message
8816 .match_indices('`')
8817 .chain([(message.len(), "")])
8818 .enumerate()
8819 {
8820 message_without_backticks.push_str(&message[prev_offset..offset]);
8821 if inside_block {
8822 highlights.extend(prev_offset - match_ix..offset - match_ix);
8823 }
8824
8825 inside_block = !inside_block;
8826 prev_offset = offset + 1;
8827 }
8828
8829 (message_without_backticks, highlights)
8830}
8831
8832pub fn diagnostic_style(
8833 severity: DiagnosticSeverity,
8834 valid: bool,
8835 theme: &theme::Editor,
8836) -> DiagnosticStyle {
8837 match (severity, valid) {
8838 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8839 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8840 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8841 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8842 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8843 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8844 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8845 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8846 _ => theme.invalid_hint_diagnostic.clone(),
8847 }
8848}
8849
8850pub fn combine_syntax_and_fuzzy_match_highlights(
8851 text: &str,
8852 default_style: HighlightStyle,
8853 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8854 match_indices: &[usize],
8855) -> Vec<(Range<usize>, HighlightStyle)> {
8856 let mut result = Vec::new();
8857 let mut match_indices = match_indices.iter().copied().peekable();
8858
8859 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8860 {
8861 syntax_highlight.weight = None;
8862
8863 // Add highlights for any fuzzy match characters before the next
8864 // syntax highlight range.
8865 while let Some(&match_index) = match_indices.peek() {
8866 if match_index >= range.start {
8867 break;
8868 }
8869 match_indices.next();
8870 let end_index = char_ix_after(match_index, text);
8871 let mut match_style = default_style;
8872 match_style.weight = Some(fonts::Weight::BOLD);
8873 result.push((match_index..end_index, match_style));
8874 }
8875
8876 if range.start == usize::MAX {
8877 break;
8878 }
8879
8880 // Add highlights for any fuzzy match characters within the
8881 // syntax highlight range.
8882 let mut offset = range.start;
8883 while let Some(&match_index) = match_indices.peek() {
8884 if match_index >= range.end {
8885 break;
8886 }
8887
8888 match_indices.next();
8889 if match_index > offset {
8890 result.push((offset..match_index, syntax_highlight));
8891 }
8892
8893 let mut end_index = char_ix_after(match_index, text);
8894 while let Some(&next_match_index) = match_indices.peek() {
8895 if next_match_index == end_index && next_match_index < range.end {
8896 end_index = char_ix_after(next_match_index, text);
8897 match_indices.next();
8898 } else {
8899 break;
8900 }
8901 }
8902
8903 let mut match_style = syntax_highlight;
8904 match_style.weight = Some(fonts::Weight::BOLD);
8905 result.push((match_index..end_index, match_style));
8906 offset = end_index;
8907 }
8908
8909 if offset < range.end {
8910 result.push((offset..range.end, syntax_highlight));
8911 }
8912 }
8913
8914 fn char_ix_after(ix: usize, text: &str) -> usize {
8915 ix + text[ix..].chars().next().unwrap().len_utf8()
8916 }
8917
8918 result
8919}
8920
8921pub fn styled_runs_for_code_label<'a>(
8922 label: &'a CodeLabel,
8923 syntax_theme: &'a theme::SyntaxTheme,
8924) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8925 let fade_out = HighlightStyle {
8926 fade_out: Some(0.35),
8927 ..Default::default()
8928 };
8929
8930 let mut prev_end = label.filter_range.end;
8931 label
8932 .runs
8933 .iter()
8934 .enumerate()
8935 .flat_map(move |(ix, (range, highlight_id))| {
8936 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8937 style
8938 } else {
8939 return Default::default();
8940 };
8941 let mut muted_style = style;
8942 muted_style.highlight(fade_out);
8943
8944 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8945 if range.start >= label.filter_range.end {
8946 if range.start > prev_end {
8947 runs.push((prev_end..range.start, fade_out));
8948 }
8949 runs.push((range.clone(), muted_style));
8950 } else if range.end <= label.filter_range.end {
8951 runs.push((range.clone(), style));
8952 } else {
8953 runs.push((range.start..label.filter_range.end, style));
8954 runs.push((label.filter_range.end..range.end, muted_style));
8955 }
8956 prev_end = cmp::max(prev_end, range.end);
8957
8958 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8959 runs.push((prev_end..label.text.len(), fade_out));
8960 }
8961
8962 runs
8963 })
8964}
8965
8966pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8967 let mut index = 0;
8968 let mut codepoints = text.char_indices().peekable();
8969
8970 std::iter::from_fn(move || {
8971 let start_index = index;
8972 while let Some((new_index, codepoint)) = codepoints.next() {
8973 index = new_index + codepoint.len_utf8();
8974 let current_upper = codepoint.is_uppercase();
8975 let next_upper = codepoints
8976 .peek()
8977 .map(|(_, c)| c.is_uppercase())
8978 .unwrap_or(false);
8979
8980 if !current_upper && next_upper {
8981 return Some(&text[start_index..index]);
8982 }
8983 }
8984
8985 index = text.len();
8986 if start_index < text.len() {
8987 return Some(&text[start_index..]);
8988 }
8989 None
8990 })
8991 .flat_map(|word| word.split_inclusive('_'))
8992}
8993
8994trait RangeToAnchorExt {
8995 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8996}
8997
8998impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8999 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9000 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9001 }
9002}