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