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