1pub mod display_map;
2mod element;
3mod highlight_matching_bracket;
4mod hover_popover;
5pub mod items;
6mod link_go_to_definition;
7mod mouse_context_menu;
8pub mod movement;
9mod multi_buffer;
10pub mod selections_collection;
11
12#[cfg(any(test, feature = "test-support"))]
13pub mod test;
14
15use aho_corasick::AhoCorasick;
16use anyhow::Result;
17use clock::ReplicaId;
18use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
19pub use display_map::DisplayPoint;
20use display_map::*;
21pub use element::*;
22use futures::FutureExt;
23use fuzzy::{StringMatch, StringMatchCandidate};
24use gpui::{
25 actions,
26 color::Color,
27 elements::*,
28 executor,
29 fonts::{self, HighlightStyle, TextStyle},
30 geometry::vector::{vec2f, Vector2F},
31 impl_actions, impl_internal_actions,
32 platform::CursorStyle,
33 serde_json::json,
34 text_layout, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox,
35 Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
36 ViewContext, ViewHandle, WeakViewHandle,
37};
38use highlight_matching_bracket::refresh_matching_bracket_highlights;
39use hover_popover::{hide_hover, HoverState};
40pub use items::MAX_TAB_TITLE_LEN;
41pub use language::{char_kind, CharKind};
42use language::{
43 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
44 DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point,
45 Selection, SelectionGoal, TransactionId,
46};
47use link_go_to_definition::{hide_link_definition, LinkGoToDefinitionState};
48pub use multi_buffer::{
49 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
50 ToPoint,
51};
52use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
53use ordered_float::OrderedFloat;
54use project::{FormatTrigger, LocationLink, Project, ProjectPath, ProjectTransaction};
55use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
56use serde::{Deserialize, Serialize};
57use settings::Settings;
58use smallvec::SmallVec;
59use smol::Timer;
60use snippet::Snippet;
61use std::{
62 any::TypeId,
63 borrow::Cow,
64 cmp::{self, Ordering, Reverse},
65 mem,
66 num::NonZeroU32,
67 ops::{Deref, DerefMut, Range, RangeInclusive},
68 sync::Arc,
69 time::{Duration, Instant},
70};
71pub use sum_tree::Bias;
72use theme::{DiagnosticStyle, Theme};
73use util::{post_inc, ResultExt, TryFutureExt};
74use workspace::{ItemNavHistory, Workspace};
75
76const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
77const MAX_LINE_LEN: usize = 1024;
78const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
79const MAX_SELECTION_HISTORY_LEN: usize = 1024;
80
81pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
82
83#[derive(Clone, Deserialize, PartialEq, Default)]
84pub struct SelectNext {
85 #[serde(default)]
86 pub replace_newest: bool,
87}
88
89#[derive(Clone, PartialEq)]
90pub struct Scroll(pub Vector2F);
91
92#[derive(Clone, PartialEq)]
93pub struct Select(pub SelectPhase);
94
95#[derive(Clone, Debug, PartialEq)]
96pub struct Jump {
97 path: ProjectPath,
98 position: Point,
99 anchor: language::Anchor,
100}
101
102#[derive(Clone, Deserialize, PartialEq)]
103pub struct SelectToBeginningOfLine {
104 #[serde(default)]
105 stop_at_soft_wraps: bool,
106}
107
108#[derive(Clone, Deserialize, PartialEq)]
109pub struct SelectToEndOfLine {
110 #[serde(default)]
111 stop_at_soft_wraps: bool,
112}
113
114#[derive(Clone, Deserialize, PartialEq)]
115pub struct ToggleCodeActions {
116 #[serde(default)]
117 pub deployed_from_indicator: bool,
118}
119
120#[derive(Clone, Default, Deserialize, PartialEq)]
121pub struct ConfirmCompletion {
122 #[serde(default)]
123 pub item_ix: Option<usize>,
124}
125
126#[derive(Clone, Default, Deserialize, PartialEq)]
127pub struct ConfirmCodeAction {
128 #[serde(default)]
129 pub item_ix: Option<usize>,
130}
131
132actions!(
133 editor,
134 [
135 Cancel,
136 Backspace,
137 Delete,
138 Newline,
139 NewlineBelow,
140 GoToDiagnostic,
141 GoToPrevDiagnostic,
142 Indent,
143 Outdent,
144 DeleteLine,
145 DeleteToPreviousWordStart,
146 DeleteToPreviousSubwordStart,
147 DeleteToNextWordEnd,
148 DeleteToNextSubwordEnd,
149 DeleteToBeginningOfLine,
150 DeleteToEndOfLine,
151 CutToEndOfLine,
152 DuplicateLine,
153 MoveLineUp,
154 MoveLineDown,
155 Transpose,
156 Cut,
157 Copy,
158 Paste,
159 Undo,
160 Redo,
161 MoveUp,
162 MoveDown,
163 MoveLeft,
164 MoveRight,
165 MoveToPreviousWordStart,
166 MoveToPreviousSubwordStart,
167 MoveToNextWordEnd,
168 MoveToNextSubwordEnd,
169 MoveToBeginningOfLine,
170 MoveToEndOfLine,
171 MoveToBeginning,
172 MoveToEnd,
173 SelectUp,
174 SelectDown,
175 SelectLeft,
176 SelectRight,
177 SelectToPreviousWordStart,
178 SelectToPreviousSubwordStart,
179 SelectToNextWordEnd,
180 SelectToNextSubwordEnd,
181 SelectToBeginning,
182 SelectToEnd,
183 SelectAll,
184 SelectLine,
185 SplitSelectionIntoLines,
186 AddSelectionAbove,
187 AddSelectionBelow,
188 Tab,
189 TabPrev,
190 ToggleComments,
191 ShowCharacterPalette,
192 SelectLargerSyntaxNode,
193 SelectSmallerSyntaxNode,
194 GoToDefinition,
195 GoToTypeDefinition,
196 MoveToEnclosingBracket,
197 UndoSelection,
198 RedoSelection,
199 FindAllReferences,
200 Rename,
201 ConfirmRename,
202 PageUp,
203 PageDown,
204 Fold,
205 UnfoldLines,
206 FoldSelectedRanges,
207 ShowCompletions,
208 OpenExcerpts,
209 RestartLanguageServer,
210 Hover,
211 Format,
212 ]
213);
214
215impl_actions!(
216 editor,
217 [
218 SelectNext,
219 SelectToBeginningOfLine,
220 SelectToEndOfLine,
221 ToggleCodeActions,
222 ConfirmCompletion,
223 ConfirmCodeAction,
224 ]
225);
226
227impl_internal_actions!(editor, [Scroll, Select, Jump]);
228
229enum DocumentHighlightRead {}
230enum DocumentHighlightWrite {}
231enum InputComposition {}
232
233#[derive(Copy, Clone, PartialEq, Eq)]
234pub enum Direction {
235 Prev,
236 Next,
237}
238
239pub fn init(cx: &mut MutableAppContext) {
240 cx.add_action(Editor::new_file);
241 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
242 cx.add_action(Editor::select);
243 cx.add_action(Editor::cancel);
244 cx.add_action(Editor::newline);
245 cx.add_action(Editor::newline_below);
246 cx.add_action(Editor::backspace);
247 cx.add_action(Editor::delete);
248 cx.add_action(Editor::tab);
249 cx.add_action(Editor::tab_prev);
250 cx.add_action(Editor::indent);
251 cx.add_action(Editor::outdent);
252 cx.add_action(Editor::delete_line);
253 cx.add_action(Editor::delete_to_previous_word_start);
254 cx.add_action(Editor::delete_to_previous_subword_start);
255 cx.add_action(Editor::delete_to_next_word_end);
256 cx.add_action(Editor::delete_to_next_subword_end);
257 cx.add_action(Editor::delete_to_beginning_of_line);
258 cx.add_action(Editor::delete_to_end_of_line);
259 cx.add_action(Editor::cut_to_end_of_line);
260 cx.add_action(Editor::duplicate_line);
261 cx.add_action(Editor::move_line_up);
262 cx.add_action(Editor::move_line_down);
263 cx.add_action(Editor::transpose);
264 cx.add_action(Editor::cut);
265 cx.add_action(Editor::copy);
266 cx.add_action(Editor::paste);
267 cx.add_action(Editor::undo);
268 cx.add_action(Editor::redo);
269 cx.add_action(Editor::move_up);
270 cx.add_action(Editor::move_down);
271 cx.add_action(Editor::move_left);
272 cx.add_action(Editor::move_right);
273 cx.add_action(Editor::move_to_previous_word_start);
274 cx.add_action(Editor::move_to_previous_subword_start);
275 cx.add_action(Editor::move_to_next_word_end);
276 cx.add_action(Editor::move_to_next_subword_end);
277 cx.add_action(Editor::move_to_beginning_of_line);
278 cx.add_action(Editor::move_to_end_of_line);
279 cx.add_action(Editor::move_to_beginning);
280 cx.add_action(Editor::move_to_end);
281 cx.add_action(Editor::select_up);
282 cx.add_action(Editor::select_down);
283 cx.add_action(Editor::select_left);
284 cx.add_action(Editor::select_right);
285 cx.add_action(Editor::select_to_previous_word_start);
286 cx.add_action(Editor::select_to_previous_subword_start);
287 cx.add_action(Editor::select_to_next_word_end);
288 cx.add_action(Editor::select_to_next_subword_end);
289 cx.add_action(Editor::select_to_beginning_of_line);
290 cx.add_action(Editor::select_to_end_of_line);
291 cx.add_action(Editor::select_to_beginning);
292 cx.add_action(Editor::select_to_end);
293 cx.add_action(Editor::select_all);
294 cx.add_action(Editor::select_line);
295 cx.add_action(Editor::split_selection_into_lines);
296 cx.add_action(Editor::add_selection_above);
297 cx.add_action(Editor::add_selection_below);
298 cx.add_action(Editor::select_next);
299 cx.add_action(Editor::toggle_comments);
300 cx.add_action(Editor::select_larger_syntax_node);
301 cx.add_action(Editor::select_smaller_syntax_node);
302 cx.add_action(Editor::move_to_enclosing_bracket);
303 cx.add_action(Editor::undo_selection);
304 cx.add_action(Editor::redo_selection);
305 cx.add_action(Editor::go_to_diagnostic);
306 cx.add_action(Editor::go_to_prev_diagnostic);
307 cx.add_action(Editor::go_to_definition);
308 cx.add_action(Editor::go_to_type_definition);
309 cx.add_action(Editor::page_up);
310 cx.add_action(Editor::page_down);
311 cx.add_action(Editor::fold);
312 cx.add_action(Editor::unfold_lines);
313 cx.add_action(Editor::fold_selected_ranges);
314 cx.add_action(Editor::show_completions);
315 cx.add_action(Editor::toggle_code_actions);
316 cx.add_action(Editor::open_excerpts);
317 cx.add_action(Editor::jump);
318 cx.add_async_action(Editor::format);
319 cx.add_action(Editor::restart_language_server);
320 cx.add_action(Editor::show_character_palette);
321 cx.add_async_action(Editor::confirm_completion);
322 cx.add_async_action(Editor::confirm_code_action);
323 cx.add_async_action(Editor::rename);
324 cx.add_async_action(Editor::confirm_rename);
325 cx.add_async_action(Editor::find_all_references);
326
327 hover_popover::init(cx);
328 link_go_to_definition::init(cx);
329 mouse_context_menu::init(cx);
330
331 workspace::register_project_item::<Editor>(cx);
332 workspace::register_followable_item::<Editor>(cx);
333}
334
335trait InvalidationRegion {
336 fn ranges(&self) -> &[Range<Anchor>];
337}
338
339#[derive(Clone, Debug, PartialEq)]
340pub enum SelectPhase {
341 Begin {
342 position: DisplayPoint,
343 add: bool,
344 click_count: usize,
345 },
346 BeginColumnar {
347 position: DisplayPoint,
348 goal_column: u32,
349 },
350 Extend {
351 position: DisplayPoint,
352 click_count: usize,
353 },
354 Update {
355 position: DisplayPoint,
356 goal_column: u32,
357 scroll_position: Vector2F,
358 },
359 End,
360}
361
362#[derive(Clone, Debug)]
363pub enum SelectMode {
364 Character,
365 Word(Range<Anchor>),
366 Line(Range<Anchor>),
367 All,
368}
369
370#[derive(PartialEq, Eq)]
371pub enum Autoscroll {
372 Fit,
373 Center,
374 Newest,
375}
376
377#[derive(Copy, Clone, PartialEq, Eq)]
378pub enum EditorMode {
379 SingleLine,
380 AutoHeight { max_lines: usize },
381 Full,
382}
383
384#[derive(Clone)]
385pub enum SoftWrap {
386 None,
387 EditorWidth,
388 Column(u32),
389}
390
391#[derive(Clone)]
392pub struct EditorStyle {
393 pub text: TextStyle,
394 pub placeholder_text: Option<TextStyle>,
395 pub theme: theme::Editor,
396}
397
398type CompletionId = usize;
399
400pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
401
402type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
403
404pub struct Editor {
405 handle: WeakViewHandle<Self>,
406 buffer: ModelHandle<MultiBuffer>,
407 display_map: ModelHandle<DisplayMap>,
408 pub selections: SelectionsCollection,
409 columnar_selection_tail: Option<Anchor>,
410 add_selections_state: Option<AddSelectionsState>,
411 select_next_state: Option<SelectNextState>,
412 selection_history: SelectionHistory,
413 autoclose_regions: Vec<AutocloseRegion>,
414 snippet_stack: InvalidationStack<SnippetState>,
415 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
416 ime_transaction: Option<TransactionId>,
417 active_diagnostics: Option<ActiveDiagnosticGroup>,
418 scroll_position: Vector2F,
419 scroll_top_anchor: Anchor,
420 autoscroll_request: Option<(Autoscroll, bool)>,
421 soft_wrap_mode_override: Option<settings::SoftWrap>,
422 get_field_editor_theme: Option<GetFieldEditorTheme>,
423 override_text_style: Option<Box<OverrideTextStyle>>,
424 project: Option<ModelHandle<Project>>,
425 focused: bool,
426 show_local_cursors: bool,
427 show_local_selections: bool,
428 blink_epoch: usize,
429 blinking_paused: bool,
430 mode: EditorMode,
431 vertical_scroll_margin: f32,
432 placeholder_text: Option<Arc<str>>,
433 highlighted_rows: Option<Range<u32>>,
434 #[allow(clippy::type_complexity)]
435 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
436 nav_history: Option<ItemNavHistory>,
437 context_menu: Option<ContextMenu>,
438 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
439 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
440 next_completion_id: CompletionId,
441 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
442 code_actions_task: Option<Task<()>>,
443 document_highlights_task: Option<Task<()>>,
444 pending_rename: Option<RenameState>,
445 searchable: bool,
446 cursor_shape: CursorShape,
447 keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
448 input_enabled: bool,
449 leader_replica_id: Option<u16>,
450 hover_state: HoverState,
451 link_go_to_definition_state: LinkGoToDefinitionState,
452 _subscriptions: Vec<Subscription>,
453}
454
455pub struct EditorSnapshot {
456 pub mode: EditorMode,
457 pub display_snapshot: DisplaySnapshot,
458 pub placeholder_text: Option<Arc<str>>,
459 is_focused: bool,
460 scroll_position: Vector2F,
461 scroll_top_anchor: Anchor,
462}
463
464#[derive(Clone, Debug)]
465struct SelectionHistoryEntry {
466 selections: Arc<[Selection<Anchor>]>,
467 select_next_state: Option<SelectNextState>,
468 add_selections_state: Option<AddSelectionsState>,
469}
470
471enum SelectionHistoryMode {
472 Normal,
473 Undoing,
474 Redoing,
475}
476
477impl Default for SelectionHistoryMode {
478 fn default() -> Self {
479 Self::Normal
480 }
481}
482
483#[derive(Default)]
484struct SelectionHistory {
485 #[allow(clippy::type_complexity)]
486 selections_by_transaction:
487 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
488 mode: SelectionHistoryMode,
489 undo_stack: VecDeque<SelectionHistoryEntry>,
490 redo_stack: VecDeque<SelectionHistoryEntry>,
491}
492
493impl SelectionHistory {
494 fn insert_transaction(
495 &mut self,
496 transaction_id: TransactionId,
497 selections: Arc<[Selection<Anchor>]>,
498 ) {
499 self.selections_by_transaction
500 .insert(transaction_id, (selections, None));
501 }
502
503 #[allow(clippy::type_complexity)]
504 fn transaction(
505 &self,
506 transaction_id: TransactionId,
507 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
508 self.selections_by_transaction.get(&transaction_id)
509 }
510
511 #[allow(clippy::type_complexity)]
512 fn transaction_mut(
513 &mut self,
514 transaction_id: TransactionId,
515 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
516 self.selections_by_transaction.get_mut(&transaction_id)
517 }
518
519 fn push(&mut self, entry: SelectionHistoryEntry) {
520 if !entry.selections.is_empty() {
521 match self.mode {
522 SelectionHistoryMode::Normal => {
523 self.push_undo(entry);
524 self.redo_stack.clear();
525 }
526 SelectionHistoryMode::Undoing => self.push_redo(entry),
527 SelectionHistoryMode::Redoing => self.push_undo(entry),
528 }
529 }
530 }
531
532 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
533 if self
534 .undo_stack
535 .back()
536 .map_or(true, |e| e.selections != entry.selections)
537 {
538 self.undo_stack.push_back(entry);
539 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
540 self.undo_stack.pop_front();
541 }
542 }
543 }
544
545 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
546 if self
547 .redo_stack
548 .back()
549 .map_or(true, |e| e.selections != entry.selections)
550 {
551 self.redo_stack.push_back(entry);
552 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
553 self.redo_stack.pop_front();
554 }
555 }
556 }
557}
558
559#[derive(Clone, Debug)]
560struct AddSelectionsState {
561 above: bool,
562 stack: Vec<usize>,
563}
564
565#[derive(Clone, Debug)]
566struct SelectNextState {
567 query: AhoCorasick,
568 wordwise: bool,
569 done: bool,
570}
571
572#[derive(Debug)]
573struct AutocloseRegion {
574 selection_id: usize,
575 range: Range<Anchor>,
576 pair: BracketPair,
577}
578
579#[derive(Debug)]
580struct SnippetState {
581 ranges: Vec<Vec<Range<Anchor>>>,
582 active_index: usize,
583}
584
585pub struct RenameState {
586 pub range: Range<Anchor>,
587 pub old_name: Arc<str>,
588 pub editor: ViewHandle<Editor>,
589 block_id: BlockId,
590}
591
592struct InvalidationStack<T>(Vec<T>);
593
594enum ContextMenu {
595 Completions(CompletionsMenu),
596 CodeActions(CodeActionsMenu),
597}
598
599impl ContextMenu {
600 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
601 if self.visible() {
602 match self {
603 ContextMenu::Completions(menu) => menu.select_prev(cx),
604 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
605 }
606 true
607 } else {
608 false
609 }
610 }
611
612 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
613 if self.visible() {
614 match self {
615 ContextMenu::Completions(menu) => menu.select_next(cx),
616 ContextMenu::CodeActions(menu) => menu.select_next(cx),
617 }
618 true
619 } else {
620 false
621 }
622 }
623
624 fn visible(&self) -> bool {
625 match self {
626 ContextMenu::Completions(menu) => menu.visible(),
627 ContextMenu::CodeActions(menu) => menu.visible(),
628 }
629 }
630
631 fn render(
632 &self,
633 cursor_position: DisplayPoint,
634 style: EditorStyle,
635 cx: &mut RenderContext<Editor>,
636 ) -> (DisplayPoint, ElementBox) {
637 match self {
638 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
639 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
640 }
641 }
642}
643
644struct CompletionsMenu {
645 id: CompletionId,
646 initial_position: Anchor,
647 buffer: ModelHandle<Buffer>,
648 completions: Arc<[Completion]>,
649 match_candidates: Vec<StringMatchCandidate>,
650 matches: Arc<[StringMatch]>,
651 selected_item: usize,
652 list: UniformListState,
653}
654
655impl CompletionsMenu {
656 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
657 if self.selected_item > 0 {
658 self.selected_item -= 1;
659 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
660 }
661 cx.notify();
662 }
663
664 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
665 if self.selected_item + 1 < self.matches.len() {
666 self.selected_item += 1;
667 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
668 }
669 cx.notify();
670 }
671
672 fn visible(&self) -> bool {
673 !self.matches.is_empty()
674 }
675
676 fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
677 enum CompletionTag {}
678
679 let completions = self.completions.clone();
680 let matches = self.matches.clone();
681 let selected_item = self.selected_item;
682 let container_style = style.autocomplete.container;
683 UniformList::new(
684 self.list.clone(),
685 matches.len(),
686 cx,
687 move |_, range, items, cx| {
688 let start_ix = range.start;
689 for (ix, mat) in matches[range].iter().enumerate() {
690 let completion = &completions[mat.candidate_id];
691 let item_ix = start_ix + ix;
692 items.push(
693 MouseEventHandler::<CompletionTag>::new(
694 mat.candidate_id,
695 cx,
696 |state, _| {
697 let item_style = if item_ix == selected_item {
698 style.autocomplete.selected_item
699 } else if state.hovered {
700 style.autocomplete.hovered_item
701 } else {
702 style.autocomplete.item
703 };
704
705 Text::new(completion.label.text.clone(), style.text.clone())
706 .with_soft_wrap(false)
707 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
708 &completion.label.text,
709 style.text.color.into(),
710 styled_runs_for_code_label(
711 &completion.label,
712 &style.syntax,
713 ),
714 &mat.positions,
715 ))
716 .contained()
717 .with_style(item_style)
718 .boxed()
719 },
720 )
721 .with_cursor_style(CursorStyle::PointingHand)
722 .on_down(MouseButton::Left, move |_, cx| {
723 cx.dispatch_action(ConfirmCompletion {
724 item_ix: Some(item_ix),
725 });
726 })
727 .boxed(),
728 );
729 }
730 },
731 )
732 .with_width_from_item(
733 self.matches
734 .iter()
735 .enumerate()
736 .max_by_key(|(_, mat)| {
737 self.completions[mat.candidate_id]
738 .label
739 .text
740 .chars()
741 .count()
742 })
743 .map(|(ix, _)| ix),
744 )
745 .contained()
746 .with_style(container_style)
747 .boxed()
748 }
749
750 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
751 let mut matches = if let Some(query) = query {
752 fuzzy::match_strings(
753 &self.match_candidates,
754 query,
755 false,
756 100,
757 &Default::default(),
758 executor,
759 )
760 .await
761 } else {
762 self.match_candidates
763 .iter()
764 .enumerate()
765 .map(|(candidate_id, candidate)| StringMatch {
766 candidate_id,
767 score: Default::default(),
768 positions: Default::default(),
769 string: candidate.string.clone(),
770 })
771 .collect()
772 };
773 matches.sort_unstable_by_key(|mat| {
774 let completion = &self.completions[mat.candidate_id];
775 (
776 completion.lsp_completion.sort_text.as_ref(),
777 Reverse(OrderedFloat(mat.score)),
778 completion.sort_key(),
779 )
780 });
781
782 for mat in &mut matches {
783 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
784 for position in &mut mat.positions {
785 *position += filter_start;
786 }
787 }
788
789 self.matches = matches.into();
790 }
791}
792
793#[derive(Clone)]
794struct CodeActionsMenu {
795 actions: Arc<[CodeAction]>,
796 buffer: ModelHandle<Buffer>,
797 selected_item: usize,
798 list: UniformListState,
799 deployed_from_indicator: bool,
800}
801
802impl CodeActionsMenu {
803 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
804 if self.selected_item > 0 {
805 self.selected_item -= 1;
806 cx.notify()
807 }
808 }
809
810 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
811 if self.selected_item + 1 < self.actions.len() {
812 self.selected_item += 1;
813 cx.notify()
814 }
815 }
816
817 fn visible(&self) -> bool {
818 !self.actions.is_empty()
819 }
820
821 fn render(
822 &self,
823 mut cursor_position: DisplayPoint,
824 style: EditorStyle,
825 cx: &mut RenderContext<Editor>,
826 ) -> (DisplayPoint, ElementBox) {
827 enum ActionTag {}
828
829 let container_style = style.autocomplete.container;
830 let actions = self.actions.clone();
831 let selected_item = self.selected_item;
832 let element = UniformList::new(
833 self.list.clone(),
834 actions.len(),
835 cx,
836 move |_, range, items, cx| {
837 let start_ix = range.start;
838 for (ix, action) in actions[range].iter().enumerate() {
839 let item_ix = start_ix + ix;
840 items.push(
841 MouseEventHandler::<ActionTag>::new(item_ix, cx, |state, _| {
842 let item_style = if item_ix == selected_item {
843 style.autocomplete.selected_item
844 } else if state.hovered {
845 style.autocomplete.hovered_item
846 } else {
847 style.autocomplete.item
848 };
849
850 Text::new(action.lsp_action.title.clone(), style.text.clone())
851 .with_soft_wrap(false)
852 .contained()
853 .with_style(item_style)
854 .boxed()
855 })
856 .with_cursor_style(CursorStyle::PointingHand)
857 .on_down(MouseButton::Left, move |_, cx| {
858 cx.dispatch_action(ConfirmCodeAction {
859 item_ix: Some(item_ix),
860 });
861 })
862 .boxed(),
863 );
864 }
865 },
866 )
867 .with_width_from_item(
868 self.actions
869 .iter()
870 .enumerate()
871 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
872 .map(|(ix, _)| ix),
873 )
874 .contained()
875 .with_style(container_style)
876 .boxed();
877
878 if self.deployed_from_indicator {
879 *cursor_position.column_mut() = 0;
880 }
881
882 (cursor_position, element)
883 }
884}
885
886#[derive(Debug)]
887struct ActiveDiagnosticGroup {
888 primary_range: Range<Anchor>,
889 primary_message: String,
890 blocks: HashMap<BlockId, Diagnostic>,
891 is_valid: bool,
892}
893
894#[derive(Serialize, Deserialize)]
895pub struct ClipboardSelection {
896 pub len: usize,
897 pub is_entire_line: bool,
898 pub first_line_indent: u32,
899}
900
901#[derive(Debug)]
902pub struct NavigationData {
903 // Matching offsets for anchor and scroll_top_anchor allows us to recreate the anchor if the buffer
904 // has since been closed
905 cursor_anchor: Anchor,
906 cursor_position: Point,
907 scroll_position: Vector2F,
908 scroll_top_anchor: Anchor,
909 scroll_top_row: u32,
910}
911
912pub struct EditorCreated(pub ViewHandle<Editor>);
913
914enum GotoDefinitionKind {
915 Symbol,
916 Type,
917}
918
919impl Editor {
920 pub fn single_line(
921 field_editor_style: Option<GetFieldEditorTheme>,
922 cx: &mut ViewContext<Self>,
923 ) -> Self {
924 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
925 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
926 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
927 }
928
929 pub fn auto_height(
930 max_lines: usize,
931 field_editor_style: Option<GetFieldEditorTheme>,
932 cx: &mut ViewContext<Self>,
933 ) -> Self {
934 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
935 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
936 Self::new(
937 EditorMode::AutoHeight { max_lines },
938 buffer,
939 None,
940 field_editor_style,
941 cx,
942 )
943 }
944
945 pub fn for_buffer(
946 buffer: ModelHandle<Buffer>,
947 project: Option<ModelHandle<Project>>,
948 cx: &mut ViewContext<Self>,
949 ) -> Self {
950 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
951 Self::new(EditorMode::Full, buffer, project, None, cx)
952 }
953
954 pub fn for_multibuffer(
955 buffer: ModelHandle<MultiBuffer>,
956 project: Option<ModelHandle<Project>>,
957 cx: &mut ViewContext<Self>,
958 ) -> Self {
959 Self::new(EditorMode::Full, buffer, project, None, cx)
960 }
961
962 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
963 let mut clone = Self::new(
964 self.mode,
965 self.buffer.clone(),
966 self.project.clone(),
967 self.get_field_editor_theme,
968 cx,
969 );
970 self.display_map.update(cx, |display_map, cx| {
971 let snapshot = display_map.snapshot(cx);
972 clone.display_map.update(cx, |display_map, cx| {
973 display_map.set_state(&snapshot, cx);
974 });
975 });
976 clone.selections.set_state(&self.selections);
977 clone.scroll_position = self.scroll_position;
978 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
979 clone.searchable = self.searchable;
980 clone
981 }
982
983 fn new(
984 mode: EditorMode,
985 buffer: ModelHandle<MultiBuffer>,
986 project: Option<ModelHandle<Project>>,
987 get_field_editor_theme: Option<GetFieldEditorTheme>,
988 cx: &mut ViewContext<Self>,
989 ) -> Self {
990 let display_map = cx.add_model(|cx| {
991 let settings = cx.global::<Settings>();
992 let style = build_style(&*settings, get_field_editor_theme, None, cx);
993 DisplayMap::new(
994 buffer.clone(),
995 style.text.font_id,
996 style.text.font_size,
997 None,
998 2,
999 1,
1000 cx,
1001 )
1002 });
1003
1004 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1005
1006 let mut this = Self {
1007 handle: cx.weak_handle(),
1008 buffer: buffer.clone(),
1009 display_map: display_map.clone(),
1010 selections,
1011 columnar_selection_tail: None,
1012 add_selections_state: None,
1013 select_next_state: None,
1014 selection_history: Default::default(),
1015 autoclose_regions: Default::default(),
1016 snippet_stack: Default::default(),
1017 select_larger_syntax_node_stack: Vec::new(),
1018 ime_transaction: Default::default(),
1019 active_diagnostics: None,
1020 soft_wrap_mode_override: None,
1021 get_field_editor_theme,
1022 project,
1023 scroll_position: Vector2F::zero(),
1024 scroll_top_anchor: Anchor::min(),
1025 autoscroll_request: None,
1026 focused: false,
1027 show_local_cursors: false,
1028 show_local_selections: true,
1029 blink_epoch: 0,
1030 blinking_paused: false,
1031 mode,
1032 vertical_scroll_margin: 3.0,
1033 placeholder_text: None,
1034 highlighted_rows: None,
1035 background_highlights: Default::default(),
1036 nav_history: None,
1037 context_menu: None,
1038 mouse_context_menu: cx.add_view(context_menu::ContextMenu::new),
1039 completion_tasks: Default::default(),
1040 next_completion_id: 0,
1041 available_code_actions: Default::default(),
1042 code_actions_task: Default::default(),
1043 document_highlights_task: Default::default(),
1044 pending_rename: Default::default(),
1045 searchable: true,
1046 override_text_style: None,
1047 cursor_shape: Default::default(),
1048 keymap_context_layers: Default::default(),
1049 input_enabled: true,
1050 leader_replica_id: None,
1051 hover_state: Default::default(),
1052 link_go_to_definition_state: Default::default(),
1053 _subscriptions: vec![
1054 cx.observe(&buffer, Self::on_buffer_changed),
1055 cx.subscribe(&buffer, Self::on_buffer_event),
1056 cx.observe(&display_map, Self::on_display_map_changed),
1057 ],
1058 };
1059 this.end_selection(cx);
1060
1061 let editor_created_event = EditorCreated(cx.handle());
1062 cx.emit_global(editor_created_event);
1063
1064 this.report_event("open editor", cx);
1065 this
1066 }
1067
1068 pub fn new_file(
1069 workspace: &mut Workspace,
1070 _: &workspace::NewFile,
1071 cx: &mut ViewContext<Workspace>,
1072 ) {
1073 let project = workspace.project().clone();
1074 if project.read(cx).is_remote() {
1075 cx.propagate_action();
1076 } else if let Some(buffer) = project
1077 .update(cx, |project, cx| project.create_buffer("", None, cx))
1078 .log_err()
1079 {
1080 workspace.add_item(
1081 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1082 cx,
1083 );
1084 }
1085 }
1086
1087 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1088 self.buffer.read(cx).replica_id()
1089 }
1090
1091 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1092 self.leader_replica_id
1093 }
1094
1095 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1096 &self.buffer
1097 }
1098
1099 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1100 self.buffer().read(cx).title(cx)
1101 }
1102
1103 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
1104 EditorSnapshot {
1105 mode: self.mode,
1106 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1107 scroll_position: self.scroll_position,
1108 scroll_top_anchor: self.scroll_top_anchor.clone(),
1109 placeholder_text: self.placeholder_text.clone(),
1110 is_focused: self
1111 .handle
1112 .upgrade(cx)
1113 .map_or(false, |handle| handle.is_focused(cx)),
1114 }
1115 }
1116
1117 pub fn language_at<'a, T: ToOffset>(
1118 &self,
1119 point: T,
1120 cx: &'a AppContext,
1121 ) -> Option<Arc<Language>> {
1122 self.buffer.read(cx).language_at(point, cx)
1123 }
1124
1125 fn style(&self, cx: &AppContext) -> EditorStyle {
1126 build_style(
1127 cx.global::<Settings>(),
1128 self.get_field_editor_theme,
1129 self.override_text_style.as_deref(),
1130 cx,
1131 )
1132 }
1133
1134 pub fn mode(&self) -> EditorMode {
1135 self.mode
1136 }
1137
1138 pub fn set_placeholder_text(
1139 &mut self,
1140 placeholder_text: impl Into<Arc<str>>,
1141 cx: &mut ViewContext<Self>,
1142 ) {
1143 self.placeholder_text = Some(placeholder_text.into());
1144 cx.notify();
1145 }
1146
1147 pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
1148 self.vertical_scroll_margin = margin_rows as f32;
1149 cx.notify();
1150 }
1151
1152 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
1153 self.set_scroll_position_internal(scroll_position, true, cx);
1154 }
1155
1156 fn set_scroll_position_internal(
1157 &mut self,
1158 scroll_position: Vector2F,
1159 local: bool,
1160 cx: &mut ViewContext<Self>,
1161 ) {
1162 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1163
1164 if scroll_position.y() == 0. {
1165 self.scroll_top_anchor = Anchor::min();
1166 self.scroll_position = scroll_position;
1167 } else {
1168 let scroll_top_buffer_offset =
1169 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
1170 let anchor = map
1171 .buffer_snapshot
1172 .anchor_at(scroll_top_buffer_offset, Bias::Right);
1173 self.scroll_position = vec2f(
1174 scroll_position.x(),
1175 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
1176 );
1177 self.scroll_top_anchor = anchor;
1178 }
1179
1180 self.autoscroll_request.take();
1181 hide_hover(self, cx);
1182
1183 cx.emit(Event::ScrollPositionChanged { local });
1184 cx.notify();
1185 }
1186
1187 fn set_scroll_top_anchor(
1188 &mut self,
1189 anchor: Anchor,
1190 position: Vector2F,
1191 cx: &mut ViewContext<Self>,
1192 ) {
1193 self.scroll_top_anchor = anchor;
1194 self.scroll_position = position;
1195 cx.emit(Event::ScrollPositionChanged { local: false });
1196 cx.notify();
1197 }
1198
1199 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1200 self.cursor_shape = cursor_shape;
1201 cx.notify();
1202 }
1203
1204 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1205 if self.display_map.read(cx).clip_at_line_ends != clip {
1206 self.display_map
1207 .update(cx, |map, _| map.clip_at_line_ends = clip);
1208 }
1209 }
1210
1211 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: gpui::keymap::Context) {
1212 self.keymap_context_layers
1213 .insert(TypeId::of::<Tag>(), context);
1214 }
1215
1216 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1217 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1218 }
1219
1220 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1221 self.input_enabled = input_enabled;
1222 }
1223
1224 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
1225 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1226 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
1227 }
1228
1229 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
1230 if max < self.scroll_position.x() {
1231 self.scroll_position.set_x(max);
1232 true
1233 } else {
1234 false
1235 }
1236 }
1237
1238 pub fn autoscroll_vertically(
1239 &mut self,
1240 viewport_height: f32,
1241 line_height: f32,
1242 cx: &mut ViewContext<Self>,
1243 ) -> bool {
1244 let visible_lines = viewport_height / line_height;
1245 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1246 let mut scroll_position =
1247 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
1248 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1249 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
1250 } else {
1251 display_map.max_point().row().saturating_sub(1) as f32
1252 };
1253 if scroll_position.y() > max_scroll_top {
1254 scroll_position.set_y(max_scroll_top);
1255 self.set_scroll_position(scroll_position, cx);
1256 }
1257
1258 let (autoscroll, local) = if let Some(autoscroll) = self.autoscroll_request.take() {
1259 autoscroll
1260 } else {
1261 return false;
1262 };
1263
1264 let first_cursor_top;
1265 let last_cursor_bottom;
1266 if let Some(highlighted_rows) = &self.highlighted_rows {
1267 first_cursor_top = highlighted_rows.start as f32;
1268 last_cursor_bottom = first_cursor_top + 1.;
1269 } else if autoscroll == Autoscroll::Newest {
1270 let newest_selection = self.selections.newest::<Point>(cx);
1271 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
1272 last_cursor_bottom = first_cursor_top + 1.;
1273 } else {
1274 let selections = self.selections.all::<Point>(cx);
1275 first_cursor_top = selections
1276 .first()
1277 .unwrap()
1278 .head()
1279 .to_display_point(&display_map)
1280 .row() as f32;
1281 last_cursor_bottom = selections
1282 .last()
1283 .unwrap()
1284 .head()
1285 .to_display_point(&display_map)
1286 .row() as f32
1287 + 1.0;
1288 }
1289
1290 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1291 0.
1292 } else {
1293 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
1294 };
1295 if margin < 0.0 {
1296 return false;
1297 }
1298
1299 match autoscroll {
1300 Autoscroll::Fit | Autoscroll::Newest => {
1301 let margin = margin.min(self.vertical_scroll_margin);
1302 let target_top = (first_cursor_top - margin).max(0.0);
1303 let target_bottom = last_cursor_bottom + margin;
1304 let start_row = scroll_position.y();
1305 let end_row = start_row + visible_lines;
1306
1307 if target_top < start_row {
1308 scroll_position.set_y(target_top);
1309 self.set_scroll_position_internal(scroll_position, local, cx);
1310 } else if target_bottom >= end_row {
1311 scroll_position.set_y(target_bottom - visible_lines);
1312 self.set_scroll_position_internal(scroll_position, local, cx);
1313 }
1314 }
1315 Autoscroll::Center => {
1316 scroll_position.set_y((first_cursor_top - margin).max(0.0));
1317 self.set_scroll_position_internal(scroll_position, local, cx);
1318 }
1319 }
1320
1321 true
1322 }
1323
1324 pub fn autoscroll_horizontally(
1325 &mut self,
1326 start_row: u32,
1327 viewport_width: f32,
1328 scroll_width: f32,
1329 max_glyph_width: f32,
1330 layouts: &[text_layout::Line],
1331 cx: &mut ViewContext<Self>,
1332 ) -> bool {
1333 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1334 let selections = self.selections.all::<Point>(cx);
1335
1336 let mut target_left;
1337 let mut target_right;
1338
1339 if self.highlighted_rows.is_some() {
1340 target_left = 0.0_f32;
1341 target_right = 0.0_f32;
1342 } else {
1343 target_left = std::f32::INFINITY;
1344 target_right = 0.0_f32;
1345 for selection in selections {
1346 let head = selection.head().to_display_point(&display_map);
1347 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
1348 let start_column = head.column().saturating_sub(3);
1349 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
1350 target_left = target_left.min(
1351 layouts[(head.row() - start_row) as usize]
1352 .x_for_index(start_column as usize),
1353 );
1354 target_right = target_right.max(
1355 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
1356 + max_glyph_width,
1357 );
1358 }
1359 }
1360 }
1361
1362 target_right = target_right.min(scroll_width);
1363
1364 if target_right - target_left > viewport_width {
1365 return false;
1366 }
1367
1368 let scroll_left = self.scroll_position.x() * max_glyph_width;
1369 let scroll_right = scroll_left + viewport_width;
1370
1371 if target_left < scroll_left {
1372 self.scroll_position.set_x(target_left / max_glyph_width);
1373 true
1374 } else if target_right > scroll_right {
1375 self.scroll_position
1376 .set_x((target_right - viewport_width) / max_glyph_width);
1377 true
1378 } else {
1379 false
1380 }
1381 }
1382
1383 fn selections_did_change(
1384 &mut self,
1385 local: bool,
1386 old_cursor_position: &Anchor,
1387 cx: &mut ViewContext<Self>,
1388 ) {
1389 if self.focused && self.leader_replica_id.is_none() {
1390 self.buffer.update(cx, |buffer, cx| {
1391 buffer.set_active_selections(
1392 &self.selections.disjoint_anchors(),
1393 self.selections.line_mode,
1394 cx,
1395 )
1396 });
1397 }
1398
1399 let display_map = self
1400 .display_map
1401 .update(cx, |display_map, cx| display_map.snapshot(cx));
1402 let buffer = &display_map.buffer_snapshot;
1403 self.add_selections_state = None;
1404 self.select_next_state = None;
1405 self.select_larger_syntax_node_stack.clear();
1406 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1407 self.snippet_stack
1408 .invalidate(&self.selections.disjoint_anchors(), buffer);
1409 self.take_rename(false, cx);
1410
1411 let new_cursor_position = self.selections.newest_anchor().head();
1412
1413 self.push_to_nav_history(
1414 old_cursor_position.clone(),
1415 Some(new_cursor_position.to_point(buffer)),
1416 cx,
1417 );
1418
1419 if local {
1420 let new_cursor_position = self.selections.newest_anchor().head();
1421 let completion_menu = match self.context_menu.as_mut() {
1422 Some(ContextMenu::Completions(menu)) => Some(menu),
1423 _ => {
1424 self.context_menu.take();
1425 None
1426 }
1427 };
1428
1429 if let Some(completion_menu) = completion_menu {
1430 let cursor_position = new_cursor_position.to_offset(buffer);
1431 let (word_range, kind) =
1432 buffer.surrounding_word(completion_menu.initial_position.clone());
1433 if kind == Some(CharKind::Word)
1434 && word_range.to_inclusive().contains(&cursor_position)
1435 {
1436 let query = Self::completion_query(buffer, cursor_position);
1437 cx.background()
1438 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1439 self.show_completions(&ShowCompletions, cx);
1440 } else {
1441 self.hide_context_menu(cx);
1442 }
1443 }
1444
1445 hide_hover(self, cx);
1446
1447 if old_cursor_position.to_display_point(&display_map).row()
1448 != new_cursor_position.to_display_point(&display_map).row()
1449 {
1450 self.available_code_actions.take();
1451 }
1452 self.refresh_code_actions(cx);
1453 self.refresh_document_highlights(cx);
1454 refresh_matching_bracket_highlights(self, cx);
1455 }
1456
1457 self.pause_cursor_blinking(cx);
1458 cx.emit(Event::SelectionsChanged { local });
1459 cx.notify();
1460 }
1461
1462 pub fn change_selections<R>(
1463 &mut self,
1464 autoscroll: Option<Autoscroll>,
1465 cx: &mut ViewContext<Self>,
1466 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1467 ) -> R {
1468 let old_cursor_position = self.selections.newest_anchor().head();
1469 self.push_to_selection_history();
1470
1471 let (changed, result) = self.selections.change_with(cx, change);
1472
1473 if changed {
1474 if let Some(autoscroll) = autoscroll {
1475 self.request_autoscroll(autoscroll, cx);
1476 }
1477 self.selections_did_change(true, &old_cursor_position, cx);
1478 }
1479
1480 result
1481 }
1482
1483 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1484 where
1485 I: IntoIterator<Item = (Range<S>, T)>,
1486 S: ToOffset,
1487 T: Into<Arc<str>>,
1488 {
1489 self.buffer
1490 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1491 }
1492
1493 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1494 where
1495 I: IntoIterator<Item = (Range<S>, T)>,
1496 S: ToOffset,
1497 T: Into<Arc<str>>,
1498 {
1499 self.buffer.update(cx, |buffer, cx| {
1500 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1501 });
1502 }
1503
1504 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1505 self.hide_context_menu(cx);
1506
1507 match phase {
1508 SelectPhase::Begin {
1509 position,
1510 add,
1511 click_count,
1512 } => self.begin_selection(*position, *add, *click_count, cx),
1513 SelectPhase::BeginColumnar {
1514 position,
1515 goal_column,
1516 } => self.begin_columnar_selection(*position, *goal_column, cx),
1517 SelectPhase::Extend {
1518 position,
1519 click_count,
1520 } => self.extend_selection(*position, *click_count, cx),
1521 SelectPhase::Update {
1522 position,
1523 goal_column,
1524 scroll_position,
1525 } => self.update_selection(*position, *goal_column, *scroll_position, cx),
1526 SelectPhase::End => self.end_selection(cx),
1527 }
1528 }
1529
1530 fn extend_selection(
1531 &mut self,
1532 position: DisplayPoint,
1533 click_count: usize,
1534 cx: &mut ViewContext<Self>,
1535 ) {
1536 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1537 let tail = self.selections.newest::<usize>(cx).tail();
1538 self.begin_selection(position, false, click_count, cx);
1539
1540 let position = position.to_offset(&display_map, Bias::Left);
1541 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1542
1543 let mut pending_selection = self
1544 .selections
1545 .pending_anchor()
1546 .expect("extend_selection not called with pending selection");
1547 if position >= tail {
1548 pending_selection.start = tail_anchor.clone();
1549 } else {
1550 pending_selection.end = tail_anchor.clone();
1551 pending_selection.reversed = true;
1552 }
1553
1554 let mut pending_mode = self.selections.pending_mode().unwrap();
1555 match &mut pending_mode {
1556 SelectMode::Word(range) | SelectMode::Line(range) => {
1557 *range = tail_anchor.clone()..tail_anchor
1558 }
1559 _ => {}
1560 }
1561
1562 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
1563 s.set_pending(pending_selection, pending_mode)
1564 });
1565 }
1566
1567 fn begin_selection(
1568 &mut self,
1569 position: DisplayPoint,
1570 add: bool,
1571 click_count: usize,
1572 cx: &mut ViewContext<Self>,
1573 ) {
1574 if !self.focused {
1575 cx.focus_self();
1576 }
1577
1578 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1579 let buffer = &display_map.buffer_snapshot;
1580 let newest_selection = self.selections.newest_anchor().clone();
1581 let position = display_map.clip_point(position, Bias::Left);
1582
1583 let start;
1584 let end;
1585 let mode;
1586 let auto_scroll;
1587 match click_count {
1588 1 => {
1589 start = buffer.anchor_before(position.to_point(&display_map));
1590 end = start.clone();
1591 mode = SelectMode::Character;
1592 auto_scroll = true;
1593 }
1594 2 => {
1595 let range = movement::surrounding_word(&display_map, position);
1596 start = buffer.anchor_before(range.start.to_point(&display_map));
1597 end = buffer.anchor_before(range.end.to_point(&display_map));
1598 mode = SelectMode::Word(start.clone()..end.clone());
1599 auto_scroll = true;
1600 }
1601 3 => {
1602 let position = display_map
1603 .clip_point(position, Bias::Left)
1604 .to_point(&display_map);
1605 let line_start = display_map.prev_line_boundary(position).0;
1606 let next_line_start = buffer.clip_point(
1607 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1608 Bias::Left,
1609 );
1610 start = buffer.anchor_before(line_start);
1611 end = buffer.anchor_before(next_line_start);
1612 mode = SelectMode::Line(start.clone()..end.clone());
1613 auto_scroll = true;
1614 }
1615 _ => {
1616 start = buffer.anchor_before(0);
1617 end = buffer.anchor_before(buffer.len());
1618 mode = SelectMode::All;
1619 auto_scroll = false;
1620 }
1621 }
1622
1623 self.change_selections(auto_scroll.then(|| Autoscroll::Newest), cx, |s| {
1624 if !add {
1625 s.clear_disjoint();
1626 } else if click_count > 1 {
1627 s.delete(newest_selection.id)
1628 }
1629
1630 s.set_pending_anchor_range(start..end, mode);
1631 });
1632 }
1633
1634 fn begin_columnar_selection(
1635 &mut self,
1636 position: DisplayPoint,
1637 goal_column: u32,
1638 cx: &mut ViewContext<Self>,
1639 ) {
1640 if !self.focused {
1641 cx.focus_self();
1642 }
1643
1644 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1645 let tail = self.selections.newest::<Point>(cx).tail();
1646 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1647
1648 self.select_columns(
1649 tail.to_display_point(&display_map),
1650 position,
1651 goal_column,
1652 &display_map,
1653 cx,
1654 );
1655 }
1656
1657 fn update_selection(
1658 &mut self,
1659 position: DisplayPoint,
1660 goal_column: u32,
1661 scroll_position: Vector2F,
1662 cx: &mut ViewContext<Self>,
1663 ) {
1664 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1665
1666 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1667 let tail = tail.to_display_point(&display_map);
1668 self.select_columns(tail, position, goal_column, &display_map, cx);
1669 } else if let Some(mut pending) = self.selections.pending_anchor() {
1670 let buffer = self.buffer.read(cx).snapshot(cx);
1671 let head;
1672 let tail;
1673 let mode = self.selections.pending_mode().unwrap();
1674 match &mode {
1675 SelectMode::Character => {
1676 head = position.to_point(&display_map);
1677 tail = pending.tail().to_point(&buffer);
1678 }
1679 SelectMode::Word(original_range) => {
1680 let original_display_range = original_range.start.to_display_point(&display_map)
1681 ..original_range.end.to_display_point(&display_map);
1682 let original_buffer_range = original_display_range.start.to_point(&display_map)
1683 ..original_display_range.end.to_point(&display_map);
1684 if movement::is_inside_word(&display_map, position)
1685 || original_display_range.contains(&position)
1686 {
1687 let word_range = movement::surrounding_word(&display_map, position);
1688 if word_range.start < original_display_range.start {
1689 head = word_range.start.to_point(&display_map);
1690 } else {
1691 head = word_range.end.to_point(&display_map);
1692 }
1693 } else {
1694 head = position.to_point(&display_map);
1695 }
1696
1697 if head <= original_buffer_range.start {
1698 tail = original_buffer_range.end;
1699 } else {
1700 tail = original_buffer_range.start;
1701 }
1702 }
1703 SelectMode::Line(original_range) => {
1704 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1705
1706 let position = display_map
1707 .clip_point(position, Bias::Left)
1708 .to_point(&display_map);
1709 let line_start = display_map.prev_line_boundary(position).0;
1710 let next_line_start = buffer.clip_point(
1711 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1712 Bias::Left,
1713 );
1714
1715 if line_start < original_range.start {
1716 head = line_start
1717 } else {
1718 head = next_line_start
1719 }
1720
1721 if head <= original_range.start {
1722 tail = original_range.end;
1723 } else {
1724 tail = original_range.start;
1725 }
1726 }
1727 SelectMode::All => {
1728 return;
1729 }
1730 };
1731
1732 if head < tail {
1733 pending.start = buffer.anchor_before(head);
1734 pending.end = buffer.anchor_before(tail);
1735 pending.reversed = true;
1736 } else {
1737 pending.start = buffer.anchor_before(tail);
1738 pending.end = buffer.anchor_before(head);
1739 pending.reversed = false;
1740 }
1741
1742 self.change_selections(None, cx, |s| {
1743 s.set_pending(pending, mode);
1744 });
1745 } else {
1746 log::error!("update_selection dispatched with no pending selection");
1747 return;
1748 }
1749
1750 self.set_scroll_position(scroll_position, cx);
1751 cx.notify();
1752 }
1753
1754 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1755 self.columnar_selection_tail.take();
1756 if self.selections.pending_anchor().is_some() {
1757 let selections = self.selections.all::<usize>(cx);
1758 self.change_selections(None, cx, |s| {
1759 s.select(selections);
1760 s.clear_pending();
1761 });
1762 }
1763 }
1764
1765 fn select_columns(
1766 &mut self,
1767 tail: DisplayPoint,
1768 head: DisplayPoint,
1769 goal_column: u32,
1770 display_map: &DisplaySnapshot,
1771 cx: &mut ViewContext<Self>,
1772 ) {
1773 let start_row = cmp::min(tail.row(), head.row());
1774 let end_row = cmp::max(tail.row(), head.row());
1775 let start_column = cmp::min(tail.column(), goal_column);
1776 let end_column = cmp::max(tail.column(), goal_column);
1777 let reversed = start_column < tail.column();
1778
1779 let selection_ranges = (start_row..=end_row)
1780 .filter_map(|row| {
1781 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1782 let start = display_map
1783 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1784 .to_point(display_map);
1785 let end = display_map
1786 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1787 .to_point(display_map);
1788 if reversed {
1789 Some(end..start)
1790 } else {
1791 Some(start..end)
1792 }
1793 } else {
1794 None
1795 }
1796 })
1797 .collect::<Vec<_>>();
1798
1799 self.change_selections(None, cx, |s| {
1800 s.select_ranges(selection_ranges);
1801 });
1802 cx.notify();
1803 }
1804
1805 pub fn has_pending_nonempty_selection(&self) -> bool {
1806 let pending_nonempty_selection = match self.selections.pending_anchor() {
1807 Some(Selection { start, end, .. }) => start != end,
1808 None => false,
1809 };
1810 pending_nonempty_selection || self.columnar_selection_tail.is_some()
1811 }
1812
1813 pub fn has_pending_selection(&self) -> bool {
1814 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1815 }
1816
1817 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1818 if self.take_rename(false, cx).is_some() {
1819 return;
1820 }
1821
1822 if hide_hover(self, cx) {
1823 return;
1824 }
1825
1826 if self.hide_context_menu(cx).is_some() {
1827 return;
1828 }
1829
1830 if self.snippet_stack.pop().is_some() {
1831 return;
1832 }
1833
1834 if self.mode == EditorMode::Full {
1835 if self.active_diagnostics.is_some() {
1836 self.dismiss_diagnostics(cx);
1837 return;
1838 }
1839
1840 if self.change_selections(Some(Autoscroll::Fit), cx, |s| s.try_cancel()) {
1841 return;
1842 }
1843 }
1844
1845 cx.propagate_action();
1846 }
1847
1848 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1849 if !self.input_enabled {
1850 return;
1851 }
1852
1853 let text: Arc<str> = text.into();
1854 let selections = self.selections.all_adjusted(cx);
1855 let mut edits = Vec::new();
1856 let mut new_selections = Vec::with_capacity(selections.len());
1857 let mut new_autoclose_regions = Vec::new();
1858 let snapshot = self.buffer.read(cx).read(cx);
1859
1860 for (selection, autoclose_region) in
1861 self.selections_with_autoclose_regions(selections, &snapshot)
1862 {
1863 if let Some(language) = snapshot.language_at(selection.head()) {
1864 // Determine if the inserted text matches the opening or closing
1865 // bracket of any of this language's bracket pairs.
1866 let mut bracket_pair = None;
1867 let mut is_bracket_pair_start = false;
1868 for pair in language.brackets() {
1869 if pair.start.ends_with(text.as_ref()) {
1870 bracket_pair = Some(pair.clone());
1871 is_bracket_pair_start = true;
1872 break;
1873 } else if pair.end.as_str() == text.as_ref() {
1874 bracket_pair = Some(pair.clone());
1875 break;
1876 }
1877 }
1878
1879 if let Some(bracket_pair) = bracket_pair {
1880 if selection.is_empty() {
1881 if is_bracket_pair_start {
1882 let prefix_len = bracket_pair.start.len() - text.len();
1883
1884 // If the inserted text is a suffix of an opening bracket and the
1885 // selection is preceded by the rest of the opening bracket, then
1886 // insert the closing bracket.
1887 let following_text_allows_autoclose = snapshot
1888 .chars_at(selection.start)
1889 .next()
1890 .map_or(true, |c| language.should_autoclose_before(c));
1891 let preceding_text_matches_prefix = prefix_len == 0
1892 || (selection.start.column >= (prefix_len as u32)
1893 && snapshot.contains_str_at(
1894 Point::new(
1895 selection.start.row,
1896 selection.start.column - (prefix_len as u32),
1897 ),
1898 &bracket_pair.start[..prefix_len],
1899 ));
1900 if following_text_allows_autoclose && preceding_text_matches_prefix {
1901 let anchor = snapshot.anchor_before(selection.end);
1902 new_selections
1903 .push((selection.map(|_| anchor.clone()), text.len()));
1904 new_autoclose_regions.push((
1905 anchor.clone(),
1906 text.len(),
1907 selection.id,
1908 bracket_pair.clone(),
1909 ));
1910 edits.push((
1911 selection.range(),
1912 format!("{}{}", text, bracket_pair.end).into(),
1913 ));
1914 continue;
1915 }
1916 } else if let Some(region) = autoclose_region {
1917 // If the selection is followed by an auto-inserted closing bracket,
1918 // then don't insert anything else; just move the selection past the
1919 // closing bracket.
1920 let should_skip = selection.end == region.range.end.to_point(&snapshot);
1921 if should_skip {
1922 let anchor = snapshot.anchor_after(selection.end);
1923 new_selections.push((
1924 selection.map(|_| anchor.clone()),
1925 region.pair.end.len(),
1926 ));
1927 continue;
1928 }
1929 }
1930 }
1931 // If an opening bracket is typed while text is selected, then
1932 // surround that text with the bracket pair.
1933 else if is_bracket_pair_start {
1934 edits.push((selection.start..selection.start, text.clone()));
1935 edits.push((
1936 selection.end..selection.end,
1937 bracket_pair.end.as_str().into(),
1938 ));
1939 new_selections.push((
1940 Selection {
1941 id: selection.id,
1942 start: snapshot.anchor_after(selection.start),
1943 end: snapshot.anchor_before(selection.end),
1944 reversed: selection.reversed,
1945 goal: selection.goal,
1946 },
1947 0,
1948 ));
1949 continue;
1950 }
1951 }
1952 }
1953
1954 // If not handling any auto-close operation, then just replace the selected
1955 // text with the given input and move the selection to the end of the
1956 // newly inserted text.
1957 let anchor = snapshot.anchor_after(selection.end);
1958 new_selections.push((selection.map(|_| anchor.clone()), 0));
1959 edits.push((selection.start..selection.end, text.clone()));
1960 }
1961
1962 drop(snapshot);
1963 self.transact(cx, |this, cx| {
1964 this.buffer.update(cx, |buffer, cx| {
1965 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
1966 });
1967
1968 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
1969 let new_selection_deltas = new_selections.iter().map(|e| e.1);
1970 let snapshot = this.buffer.read(cx).read(cx);
1971 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
1972 .zip(new_selection_deltas)
1973 .map(|(selection, delta)| selection.map(|e| e + delta))
1974 .collect::<Vec<_>>();
1975
1976 let mut i = 0;
1977 for (position, delta, selection_id, pair) in new_autoclose_regions {
1978 let position = position.to_offset(&snapshot) + delta;
1979 let start = snapshot.anchor_before(position);
1980 let end = snapshot.anchor_after(position);
1981 while let Some(existing_state) = this.autoclose_regions.get(i) {
1982 match existing_state.range.start.cmp(&start, &snapshot) {
1983 Ordering::Less => i += 1,
1984 Ordering::Greater => break,
1985 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
1986 Ordering::Less => i += 1,
1987 Ordering::Equal => break,
1988 Ordering::Greater => break,
1989 },
1990 }
1991 }
1992 this.autoclose_regions.insert(
1993 i,
1994 AutocloseRegion {
1995 selection_id,
1996 range: start..end,
1997 pair,
1998 },
1999 );
2000 }
2001
2002 drop(snapshot);
2003 this.change_selections(None, cx, |s| s.select(new_selections));
2004 this.trigger_completion_on_input(&text, cx);
2005 });
2006 }
2007
2008 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2009 self.transact(cx, |this, cx| {
2010 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2011 let selections = this.selections.all::<usize>(cx);
2012
2013 let buffer = this.buffer.read(cx).snapshot(cx);
2014 selections
2015 .iter()
2016 .map(|selection| {
2017 let start_point = selection.start.to_point(&buffer);
2018 let mut indent = buffer.indent_size_for_line(start_point.row);
2019 indent.len = cmp::min(indent.len, start_point.column);
2020 let start = selection.start;
2021 let end = selection.end;
2022
2023 let mut insert_extra_newline = false;
2024 if let Some(language) = buffer.language() {
2025 let leading_whitespace_len = buffer
2026 .reversed_chars_at(start)
2027 .take_while(|c| c.is_whitespace() && *c != '\n')
2028 .map(|c| c.len_utf8())
2029 .sum::<usize>();
2030
2031 let trailing_whitespace_len = buffer
2032 .chars_at(end)
2033 .take_while(|c| c.is_whitespace() && *c != '\n')
2034 .map(|c| c.len_utf8())
2035 .sum::<usize>();
2036
2037 insert_extra_newline = language.brackets().iter().any(|pair| {
2038 let pair_start = pair.start.trim_end();
2039 let pair_end = pair.end.trim_start();
2040
2041 pair.newline
2042 && buffer
2043 .contains_str_at(end + trailing_whitespace_len, pair_end)
2044 && buffer.contains_str_at(
2045 (start - leading_whitespace_len)
2046 .saturating_sub(pair_start.len()),
2047 pair_start,
2048 )
2049 });
2050 }
2051
2052 let mut new_text = String::with_capacity(1 + indent.len as usize);
2053 new_text.push('\n');
2054 new_text.extend(indent.chars());
2055 if insert_extra_newline {
2056 new_text = new_text.repeat(2);
2057 }
2058
2059 let anchor = buffer.anchor_after(end);
2060 let new_selection = selection.map(|_| anchor.clone());
2061 (
2062 (start..end, new_text),
2063 (insert_extra_newline, new_selection),
2064 )
2065 })
2066 .unzip()
2067 };
2068
2069 this.edit_with_autoindent(edits, cx);
2070 let buffer = this.buffer.read(cx).snapshot(cx);
2071 let new_selections = selection_fixup_info
2072 .into_iter()
2073 .map(|(extra_newline_inserted, new_selection)| {
2074 let mut cursor = new_selection.end.to_point(&buffer);
2075 if extra_newline_inserted {
2076 cursor.row -= 1;
2077 cursor.column = buffer.line_len(cursor.row);
2078 }
2079 new_selection.map(|_| cursor)
2080 })
2081 .collect();
2082
2083 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
2084 });
2085 }
2086
2087 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2088 let buffer = self.buffer.read(cx);
2089 let snapshot = buffer.snapshot(cx);
2090
2091 let mut edits = Vec::new();
2092 let mut rows = Vec::new();
2093 let mut rows_inserted = 0;
2094
2095 for selection in self.selections.all_adjusted(cx) {
2096 let cursor = selection.head();
2097 let row = cursor.row;
2098
2099 let end_of_line = snapshot
2100 .clip_point(Point::new(row, snapshot.line_len(row)), Bias::Left)
2101 .to_point(&snapshot);
2102
2103 let newline = "\n".to_string();
2104 edits.push((end_of_line..end_of_line, newline));
2105
2106 rows_inserted += 1;
2107 rows.push(row + rows_inserted);
2108 }
2109
2110 self.transact(cx, |editor, cx| {
2111 editor.edit_with_autoindent(edits, cx);
2112
2113 editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
2114 let mut index = 0;
2115 s.move_cursors_with(|map, _, _| {
2116 let row = rows[index];
2117 index += 1;
2118
2119 let point = Point::new(row, 0);
2120 let boundary = map.next_line_boundary(point).1;
2121 let clipped = map.clip_point(boundary, Bias::Left);
2122
2123 (clipped, SelectionGoal::None)
2124 });
2125 });
2126 });
2127 }
2128
2129 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2130 let text: Arc<str> = text.into();
2131 self.transact(cx, |this, cx| {
2132 let old_selections = this.selections.all_adjusted(cx);
2133 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2134 let anchors = {
2135 let snapshot = buffer.read(cx);
2136 old_selections
2137 .iter()
2138 .map(|s| {
2139 let anchor = snapshot.anchor_after(s.end);
2140 s.map(|_| anchor.clone())
2141 })
2142 .collect::<Vec<_>>()
2143 };
2144 buffer.edit(
2145 old_selections
2146 .iter()
2147 .map(|s| (s.start..s.end, text.clone())),
2148 Some(AutoindentMode::EachLine),
2149 cx,
2150 );
2151 anchors
2152 });
2153
2154 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2155 s.select_anchors(selection_anchors);
2156 })
2157 });
2158 }
2159
2160 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2161 if !cx.global::<Settings>().show_completions_on_input {
2162 return;
2163 }
2164
2165 let selection = self.selections.newest_anchor();
2166 if self
2167 .buffer
2168 .read(cx)
2169 .is_completion_trigger(selection.head(), text, cx)
2170 {
2171 self.show_completions(&ShowCompletions, cx);
2172 } else {
2173 self.hide_context_menu(cx);
2174 }
2175 }
2176
2177 /// If any empty selections is touching the start of its innermost containing autoclose
2178 /// region, expand it to select the brackets.
2179 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2180 let selections = self.selections.all::<usize>(cx);
2181 let buffer = self.buffer.read(cx).read(cx);
2182 let mut new_selections = Vec::new();
2183 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2184 if let (Some(region), true) = (region, selection.is_empty()) {
2185 let mut range = region.range.to_offset(&buffer);
2186 if selection.start == range.start {
2187 if range.start >= region.pair.start.len() {
2188 range.start -= region.pair.start.len();
2189 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2190 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2191 range.end += region.pair.end.len();
2192 selection.start = range.start;
2193 selection.end = range.end;
2194 }
2195 }
2196 }
2197 }
2198 }
2199 new_selections.push(selection);
2200 }
2201
2202 drop(buffer);
2203 self.change_selections(None, cx, |selections| selections.select(new_selections));
2204 }
2205
2206 /// Iterate the given selections, and for each one, find the smallest surrounding
2207 /// autoclose region. This uses the ordering of the selections and the autoclose
2208 /// regions to avoid repeated comparisons.
2209 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2210 &'a self,
2211 selections: impl IntoIterator<Item = Selection<D>>,
2212 buffer: &'a MultiBufferSnapshot,
2213 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2214 let mut i = 0;
2215 let mut regions = self.autoclose_regions.as_slice();
2216 selections.into_iter().map(move |selection| {
2217 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2218
2219 let mut enclosing = None;
2220 while let Some(pair_state) = regions.get(i) {
2221 if pair_state.range.end.to_offset(buffer) < range.start {
2222 regions = ®ions[i + 1..];
2223 i = 0;
2224 } else if pair_state.range.start.to_offset(buffer) > range.end {
2225 break;
2226 } else if pair_state.selection_id == selection.id {
2227 enclosing = Some(pair_state);
2228 i += 1;
2229 }
2230 }
2231
2232 (selection.clone(), enclosing)
2233 })
2234 }
2235
2236 /// Remove any autoclose regions that no longer contain their selection.
2237 fn invalidate_autoclose_regions(
2238 &mut self,
2239 mut selections: &[Selection<Anchor>],
2240 buffer: &MultiBufferSnapshot,
2241 ) {
2242 self.autoclose_regions.retain(|state| {
2243 let mut i = 0;
2244 while let Some(selection) = selections.get(i) {
2245 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2246 selections = &selections[1..];
2247 continue;
2248 }
2249 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2250 break;
2251 }
2252 if selection.id == state.selection_id {
2253 return true;
2254 } else {
2255 i += 1;
2256 }
2257 }
2258 false
2259 });
2260 }
2261
2262 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2263 let offset = position.to_offset(buffer);
2264 let (word_range, kind) = buffer.surrounding_word(offset);
2265 if offset > word_range.start && kind == Some(CharKind::Word) {
2266 Some(
2267 buffer
2268 .text_for_range(word_range.start..offset)
2269 .collect::<String>(),
2270 )
2271 } else {
2272 None
2273 }
2274 }
2275
2276 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2277 if self.pending_rename.is_some() {
2278 return;
2279 }
2280
2281 let project = if let Some(project) = self.project.clone() {
2282 project
2283 } else {
2284 return;
2285 };
2286
2287 let position = self.selections.newest_anchor().head();
2288 let (buffer, buffer_position) = if let Some(output) = self
2289 .buffer
2290 .read(cx)
2291 .text_anchor_for_position(position.clone(), cx)
2292 {
2293 output
2294 } else {
2295 return;
2296 };
2297
2298 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2299 let completions = project.update(cx, |project, cx| {
2300 project.completions(&buffer, buffer_position, cx)
2301 });
2302
2303 let id = post_inc(&mut self.next_completion_id);
2304 let task = cx.spawn_weak(|this, mut cx| {
2305 async move {
2306 let completions = completions.await?;
2307 if completions.is_empty() {
2308 return Ok(());
2309 }
2310
2311 let mut menu = CompletionsMenu {
2312 id,
2313 initial_position: position,
2314 match_candidates: completions
2315 .iter()
2316 .enumerate()
2317 .map(|(id, completion)| {
2318 StringMatchCandidate::new(
2319 id,
2320 completion.label.text[completion.label.filter_range.clone()].into(),
2321 )
2322 })
2323 .collect(),
2324 buffer,
2325 completions: completions.into(),
2326 matches: Vec::new().into(),
2327 selected_item: 0,
2328 list: Default::default(),
2329 };
2330
2331 menu.filter(query.as_deref(), cx.background()).await;
2332
2333 if let Some(this) = this.upgrade(&cx) {
2334 this.update(&mut cx, |this, cx| {
2335 match this.context_menu.as_ref() {
2336 None => {}
2337 Some(ContextMenu::Completions(prev_menu)) => {
2338 if prev_menu.id > menu.id {
2339 return;
2340 }
2341 }
2342 _ => return,
2343 }
2344
2345 this.completion_tasks.retain(|(id, _)| *id > menu.id);
2346 if this.focused {
2347 this.show_context_menu(ContextMenu::Completions(menu), cx);
2348 }
2349
2350 cx.notify();
2351 });
2352 }
2353 Ok::<_, anyhow::Error>(())
2354 }
2355 .log_err()
2356 });
2357 self.completion_tasks.push((id, task));
2358 }
2359
2360 pub fn confirm_completion(
2361 &mut self,
2362 action: &ConfirmCompletion,
2363 cx: &mut ViewContext<Self>,
2364 ) -> Option<Task<Result<()>>> {
2365 use language::ToOffset as _;
2366
2367 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2368 menu
2369 } else {
2370 return None;
2371 };
2372
2373 let mat = completions_menu
2374 .matches
2375 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2376 let buffer_handle = completions_menu.buffer;
2377 let completion = completions_menu.completions.get(mat.candidate_id)?;
2378
2379 let snippet;
2380 let text;
2381 if completion.is_snippet() {
2382 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2383 text = snippet.as_ref().unwrap().text.clone();
2384 } else {
2385 snippet = None;
2386 text = completion.new_text.clone();
2387 };
2388 let selections = self.selections.all::<usize>(cx);
2389 let buffer = buffer_handle.read(cx);
2390 let old_range = completion.old_range.to_offset(buffer);
2391 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2392
2393 let newest_selection = self.selections.newest_anchor();
2394 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2395 return None;
2396 }
2397
2398 let lookbehind = newest_selection
2399 .start
2400 .text_anchor
2401 .to_offset(buffer)
2402 .saturating_sub(old_range.start);
2403 let lookahead = old_range
2404 .end
2405 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2406 let mut common_prefix_len = old_text
2407 .bytes()
2408 .zip(text.bytes())
2409 .take_while(|(a, b)| a == b)
2410 .count();
2411
2412 let snapshot = self.buffer.read(cx).snapshot(cx);
2413 let mut ranges = Vec::new();
2414 for selection in &selections {
2415 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2416 let start = selection.start.saturating_sub(lookbehind);
2417 let end = selection.end + lookahead;
2418 ranges.push(start + common_prefix_len..end);
2419 } else {
2420 common_prefix_len = 0;
2421 ranges.clear();
2422 ranges.extend(selections.iter().map(|s| {
2423 if s.id == newest_selection.id {
2424 old_range.clone()
2425 } else {
2426 s.start..s.end
2427 }
2428 }));
2429 break;
2430 }
2431 }
2432 let text = &text[common_prefix_len..];
2433
2434 self.transact(cx, |this, cx| {
2435 if let Some(mut snippet) = snippet {
2436 snippet.text = text.to_string();
2437 for tabstop in snippet.tabstops.iter_mut().flatten() {
2438 tabstop.start -= common_prefix_len as isize;
2439 tabstop.end -= common_prefix_len as isize;
2440 }
2441
2442 this.insert_snippet(&ranges, snippet, cx).log_err();
2443 } else {
2444 this.buffer.update(cx, |buffer, cx| {
2445 buffer.edit(
2446 ranges.iter().map(|range| (range.clone(), text)),
2447 Some(AutoindentMode::EachLine),
2448 cx,
2449 );
2450 });
2451 }
2452 });
2453
2454 let project = self.project.clone()?;
2455 let apply_edits = project.update(cx, |project, cx| {
2456 project.apply_additional_edits_for_completion(
2457 buffer_handle,
2458 completion.clone(),
2459 true,
2460 cx,
2461 )
2462 });
2463 Some(cx.foreground().spawn(async move {
2464 apply_edits.await?;
2465 Ok(())
2466 }))
2467 }
2468
2469 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2470 if matches!(
2471 self.context_menu.as_ref(),
2472 Some(ContextMenu::CodeActions(_))
2473 ) {
2474 self.context_menu.take();
2475 cx.notify();
2476 return;
2477 }
2478
2479 let deployed_from_indicator = action.deployed_from_indicator;
2480 let mut task = self.code_actions_task.take();
2481 cx.spawn_weak(|this, mut cx| async move {
2482 while let Some(prev_task) = task {
2483 prev_task.await;
2484 task = this
2485 .upgrade(&cx)
2486 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2487 }
2488
2489 if let Some(this) = this.upgrade(&cx) {
2490 this.update(&mut cx, |this, cx| {
2491 if this.focused {
2492 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2493 this.show_context_menu(
2494 ContextMenu::CodeActions(CodeActionsMenu {
2495 buffer,
2496 actions,
2497 selected_item: Default::default(),
2498 list: Default::default(),
2499 deployed_from_indicator,
2500 }),
2501 cx,
2502 );
2503 }
2504 }
2505 })
2506 }
2507 Ok::<_, anyhow::Error>(())
2508 })
2509 .detach_and_log_err(cx);
2510 }
2511
2512 pub fn confirm_code_action(
2513 workspace: &mut Workspace,
2514 action: &ConfirmCodeAction,
2515 cx: &mut ViewContext<Workspace>,
2516 ) -> Option<Task<Result<()>>> {
2517 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2518 let actions_menu = if let ContextMenu::CodeActions(menu) =
2519 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2520 {
2521 menu
2522 } else {
2523 return None;
2524 };
2525 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2526 let action = actions_menu.actions.get(action_ix)?.clone();
2527 let title = action.lsp_action.title.clone();
2528 let buffer = actions_menu.buffer;
2529
2530 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2531 project.apply_code_action(buffer, action, true, cx)
2532 });
2533 Some(cx.spawn(|workspace, cx| async move {
2534 let project_transaction = apply_code_actions.await?;
2535 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2536 }))
2537 }
2538
2539 async fn open_project_transaction(
2540 this: ViewHandle<Editor>,
2541 workspace: ViewHandle<Workspace>,
2542 transaction: ProjectTransaction,
2543 title: String,
2544 mut cx: AsyncAppContext,
2545 ) -> Result<()> {
2546 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2547
2548 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2549 entries.sort_unstable_by_key(|(buffer, _)| {
2550 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2551 });
2552
2553 // If the project transaction's edits are all contained within this editor, then
2554 // avoid opening a new editor to display them.
2555
2556 if let Some((buffer, transaction)) = entries.first() {
2557 if entries.len() == 1 {
2558 let excerpt = this.read_with(&cx, |editor, cx| {
2559 editor
2560 .buffer()
2561 .read(cx)
2562 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2563 });
2564 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2565 if excerpted_buffer == *buffer {
2566 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2567 let excerpt_range = excerpt_range.to_offset(buffer);
2568 buffer
2569 .edited_ranges_for_transaction(transaction)
2570 .all(|range| {
2571 excerpt_range.start <= range.start
2572 && excerpt_range.end >= range.end
2573 })
2574 });
2575
2576 if all_edits_within_excerpt {
2577 return Ok(());
2578 }
2579 }
2580 }
2581 }
2582 } else {
2583 return Ok(());
2584 }
2585
2586 let mut ranges_to_highlight = Vec::new();
2587 let excerpt_buffer = cx.add_model(|cx| {
2588 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2589 for (buffer_handle, transaction) in &entries {
2590 let buffer = buffer_handle.read(cx);
2591 ranges_to_highlight.extend(
2592 multibuffer.push_excerpts_with_context_lines(
2593 buffer_handle.clone(),
2594 buffer
2595 .edited_ranges_for_transaction::<usize>(transaction)
2596 .collect(),
2597 1,
2598 cx,
2599 ),
2600 );
2601 }
2602 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2603 multibuffer
2604 });
2605
2606 workspace.update(&mut cx, |workspace, cx| {
2607 let project = workspace.project().clone();
2608 let editor =
2609 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2610 workspace.add_item(Box::new(editor.clone()), cx);
2611 editor.update(cx, |editor, cx| {
2612 editor.highlight_background::<Self>(
2613 ranges_to_highlight,
2614 |theme| theme.editor.highlighted_line_background,
2615 cx,
2616 );
2617 });
2618 });
2619
2620 Ok(())
2621 }
2622
2623 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2624 let project = self.project.as_ref()?;
2625 let buffer = self.buffer.read(cx);
2626 let newest_selection = self.selections.newest_anchor().clone();
2627 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2628 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2629 if start_buffer != end_buffer {
2630 return None;
2631 }
2632
2633 let actions = project.update(cx, |project, cx| {
2634 project.code_actions(&start_buffer, start..end, cx)
2635 });
2636 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2637 let actions = actions.await;
2638 if let Some(this) = this.upgrade(&cx) {
2639 this.update(&mut cx, |this, cx| {
2640 this.available_code_actions = actions.log_err().and_then(|actions| {
2641 if actions.is_empty() {
2642 None
2643 } else {
2644 Some((start_buffer, actions.into()))
2645 }
2646 });
2647 cx.notify();
2648 })
2649 }
2650 }));
2651 None
2652 }
2653
2654 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2655 if self.pending_rename.is_some() {
2656 return None;
2657 }
2658
2659 let project = self.project.as_ref()?;
2660 let buffer = self.buffer.read(cx);
2661 let newest_selection = self.selections.newest_anchor().clone();
2662 let cursor_position = newest_selection.head();
2663 let (cursor_buffer, cursor_buffer_position) =
2664 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2665 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2666 if cursor_buffer != tail_buffer {
2667 return None;
2668 }
2669
2670 let highlights = project.update(cx, |project, cx| {
2671 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2672 });
2673
2674 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2675 let highlights = highlights.log_err().await;
2676 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2677 this.update(&mut cx, |this, cx| {
2678 if this.pending_rename.is_some() {
2679 return;
2680 }
2681
2682 let buffer_id = cursor_position.buffer_id;
2683 let buffer = this.buffer.read(cx);
2684 if !buffer
2685 .text_anchor_for_position(cursor_position, cx)
2686 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2687 {
2688 return;
2689 }
2690
2691 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2692 let mut write_ranges = Vec::new();
2693 let mut read_ranges = Vec::new();
2694 for highlight in highlights {
2695 for (excerpt_id, excerpt_range) in
2696 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2697 {
2698 let start = highlight
2699 .range
2700 .start
2701 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2702 let end = highlight
2703 .range
2704 .end
2705 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2706 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2707 continue;
2708 }
2709
2710 let range = Anchor {
2711 buffer_id,
2712 excerpt_id: excerpt_id.clone(),
2713 text_anchor: start,
2714 }..Anchor {
2715 buffer_id,
2716 excerpt_id,
2717 text_anchor: end,
2718 };
2719 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2720 write_ranges.push(range);
2721 } else {
2722 read_ranges.push(range);
2723 }
2724 }
2725 }
2726
2727 this.highlight_background::<DocumentHighlightRead>(
2728 read_ranges,
2729 |theme| theme.editor.document_highlight_read_background,
2730 cx,
2731 );
2732 this.highlight_background::<DocumentHighlightWrite>(
2733 write_ranges,
2734 |theme| theme.editor.document_highlight_write_background,
2735 cx,
2736 );
2737 cx.notify();
2738 });
2739 }
2740 }));
2741 None
2742 }
2743
2744 pub fn render_code_actions_indicator(
2745 &self,
2746 style: &EditorStyle,
2747 cx: &mut RenderContext<Self>,
2748 ) -> Option<ElementBox> {
2749 if self.available_code_actions.is_some() {
2750 enum Tag {}
2751 Some(
2752 MouseEventHandler::<Tag>::new(0, cx, |_, _| {
2753 Svg::new("icons/bolt_8.svg")
2754 .with_color(style.code_actions.indicator)
2755 .boxed()
2756 })
2757 .with_cursor_style(CursorStyle::PointingHand)
2758 .with_padding(Padding::uniform(3.))
2759 .on_down(MouseButton::Left, |_, cx| {
2760 cx.dispatch_action(ToggleCodeActions {
2761 deployed_from_indicator: true,
2762 });
2763 })
2764 .boxed(),
2765 )
2766 } else {
2767 None
2768 }
2769 }
2770
2771 pub fn context_menu_visible(&self) -> bool {
2772 self.context_menu
2773 .as_ref()
2774 .map_or(false, |menu| menu.visible())
2775 }
2776
2777 pub fn render_context_menu(
2778 &self,
2779 cursor_position: DisplayPoint,
2780 style: EditorStyle,
2781 cx: &mut RenderContext<Editor>,
2782 ) -> Option<(DisplayPoint, ElementBox)> {
2783 self.context_menu
2784 .as_ref()
2785 .map(|menu| menu.render(cursor_position, style, cx))
2786 }
2787
2788 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2789 if !matches!(menu, ContextMenu::Completions(_)) {
2790 self.completion_tasks.clear();
2791 }
2792 self.context_menu = Some(menu);
2793 cx.notify();
2794 }
2795
2796 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2797 cx.notify();
2798 self.completion_tasks.clear();
2799 self.context_menu.take()
2800 }
2801
2802 pub fn insert_snippet(
2803 &mut self,
2804 insertion_ranges: &[Range<usize>],
2805 snippet: Snippet,
2806 cx: &mut ViewContext<Self>,
2807 ) -> Result<()> {
2808 let tabstops = self.buffer.update(cx, |buffer, cx| {
2809 let snippet_text: Arc<str> = snippet.text.clone().into();
2810 buffer.edit(
2811 insertion_ranges
2812 .iter()
2813 .cloned()
2814 .map(|range| (range, snippet_text.clone())),
2815 Some(AutoindentMode::EachLine),
2816 cx,
2817 );
2818
2819 let snapshot = &*buffer.read(cx);
2820 let snippet = &snippet;
2821 snippet
2822 .tabstops
2823 .iter()
2824 .map(|tabstop| {
2825 let mut tabstop_ranges = tabstop
2826 .iter()
2827 .flat_map(|tabstop_range| {
2828 let mut delta = 0_isize;
2829 insertion_ranges.iter().map(move |insertion_range| {
2830 let insertion_start = insertion_range.start as isize + delta;
2831 delta +=
2832 snippet.text.len() as isize - insertion_range.len() as isize;
2833
2834 let start = snapshot.anchor_before(
2835 (insertion_start + tabstop_range.start) as usize,
2836 );
2837 let end = snapshot
2838 .anchor_after((insertion_start + tabstop_range.end) as usize);
2839 start..end
2840 })
2841 })
2842 .collect::<Vec<_>>();
2843 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2844 tabstop_ranges
2845 })
2846 .collect::<Vec<_>>()
2847 });
2848
2849 if let Some(tabstop) = tabstops.first() {
2850 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2851 s.select_ranges(tabstop.iter().cloned());
2852 });
2853 self.snippet_stack.push(SnippetState {
2854 active_index: 0,
2855 ranges: tabstops,
2856 });
2857 }
2858
2859 Ok(())
2860 }
2861
2862 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2863 self.move_to_snippet_tabstop(Bias::Right, cx)
2864 }
2865
2866 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2867 self.move_to_snippet_tabstop(Bias::Left, cx)
2868 }
2869
2870 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2871 if let Some(mut snippet) = self.snippet_stack.pop() {
2872 match bias {
2873 Bias::Left => {
2874 if snippet.active_index > 0 {
2875 snippet.active_index -= 1;
2876 } else {
2877 self.snippet_stack.push(snippet);
2878 return false;
2879 }
2880 }
2881 Bias::Right => {
2882 if snippet.active_index + 1 < snippet.ranges.len() {
2883 snippet.active_index += 1;
2884 } else {
2885 self.snippet_stack.push(snippet);
2886 return false;
2887 }
2888 }
2889 }
2890 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2891 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2892 s.select_anchor_ranges(current_ranges.iter().cloned())
2893 });
2894 // If snippet state is not at the last tabstop, push it back on the stack
2895 if snippet.active_index + 1 < snippet.ranges.len() {
2896 self.snippet_stack.push(snippet);
2897 }
2898 return true;
2899 }
2900 }
2901
2902 false
2903 }
2904
2905 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2906 self.transact(cx, |this, cx| {
2907 this.select_all(&SelectAll, cx);
2908 this.insert("", cx);
2909 });
2910 }
2911
2912 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2913 self.transact(cx, |this, cx| {
2914 this.select_autoclose_pair(cx);
2915 let mut selections = this.selections.all::<Point>(cx);
2916 if !this.selections.line_mode {
2917 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2918 for selection in &mut selections {
2919 if selection.is_empty() {
2920 let old_head = selection.head();
2921 let mut new_head =
2922 movement::left(&display_map, old_head.to_display_point(&display_map))
2923 .to_point(&display_map);
2924 if let Some((buffer, line_buffer_range)) = display_map
2925 .buffer_snapshot
2926 .buffer_line_for_row(old_head.row)
2927 {
2928 let indent_size =
2929 buffer.indent_size_for_line(line_buffer_range.start.row);
2930 let language_name = buffer.language().map(|language| language.name());
2931 let indent_len = match indent_size.kind {
2932 IndentKind::Space => {
2933 cx.global::<Settings>().tab_size(language_name.as_deref())
2934 }
2935 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
2936 };
2937 if old_head.column <= indent_size.len && old_head.column > 0 {
2938 let indent_len = indent_len.get();
2939 new_head = cmp::min(
2940 new_head,
2941 Point::new(
2942 old_head.row,
2943 ((old_head.column - 1) / indent_len) * indent_len,
2944 ),
2945 );
2946 }
2947 }
2948
2949 selection.set_head(new_head, SelectionGoal::None);
2950 }
2951 }
2952 }
2953
2954 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2955 this.insert("", cx);
2956 });
2957 }
2958
2959 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2960 self.transact(cx, |this, cx| {
2961 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2962 let line_mode = s.line_mode;
2963 s.move_with(|map, selection| {
2964 if selection.is_empty() && !line_mode {
2965 let cursor = movement::right(map, selection.head());
2966 selection.set_head(cursor, SelectionGoal::None);
2967 }
2968 })
2969 });
2970 this.insert("", cx);
2971 });
2972 }
2973
2974 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2975 if self.move_to_prev_snippet_tabstop(cx) {
2976 return;
2977 }
2978
2979 self.outdent(&Outdent, cx);
2980 }
2981
2982 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2983 if self.move_to_next_snippet_tabstop(cx) {
2984 return;
2985 }
2986
2987 let mut selections = self.selections.all_adjusted(cx);
2988 let buffer = self.buffer.read(cx);
2989 let snapshot = buffer.snapshot(cx);
2990 let rows_iter = selections.iter().map(|s| s.head().row);
2991 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
2992
2993 let mut edits = Vec::new();
2994 let mut prev_edited_row = 0;
2995 let mut row_delta = 0;
2996 for selection in &mut selections {
2997 if selection.start.row != prev_edited_row {
2998 row_delta = 0;
2999 }
3000 prev_edited_row = selection.end.row;
3001
3002 // If the selection is non-empty, then increase the indentation of the selected lines.
3003 if !selection.is_empty() {
3004 row_delta =
3005 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3006 continue;
3007 }
3008
3009 // If the selection is empty and the cursor is in the leading whitespace before the
3010 // suggested indentation, then auto-indent the line.
3011 let cursor = selection.head();
3012 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3013 let current_indent = snapshot.indent_size_for_line(cursor.row);
3014 if cursor.column < suggested_indent.len
3015 && cursor.column <= current_indent.len
3016 && current_indent.len <= suggested_indent.len
3017 {
3018 selection.start = Point::new(cursor.row, suggested_indent.len);
3019 selection.end = selection.start;
3020 if row_delta == 0 {
3021 edits.extend(Buffer::edit_for_indent_size_adjustment(
3022 cursor.row,
3023 current_indent,
3024 suggested_indent,
3025 ));
3026 row_delta = suggested_indent.len - current_indent.len;
3027 }
3028 continue;
3029 }
3030 }
3031
3032 // Otherwise, insert a hard or soft tab.
3033 let settings = cx.global::<Settings>();
3034 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3035 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3036 IndentSize::tab()
3037 } else {
3038 let tab_size = settings.tab_size(language_name.as_deref()).get();
3039 let char_column = snapshot
3040 .text_for_range(Point::new(cursor.row, 0)..cursor)
3041 .flat_map(str::chars)
3042 .count()
3043 + row_delta as usize;
3044 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3045 IndentSize::spaces(chars_to_next_tab_stop)
3046 };
3047 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3048 selection.end = selection.start;
3049 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3050 row_delta += tab_size.len;
3051 }
3052
3053 self.transact(cx, |this, cx| {
3054 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3055 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections))
3056 });
3057 }
3058
3059 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3060 let mut selections = self.selections.all::<Point>(cx);
3061 let mut prev_edited_row = 0;
3062 let mut row_delta = 0;
3063 let mut edits = Vec::new();
3064 let buffer = self.buffer.read(cx);
3065 let snapshot = buffer.snapshot(cx);
3066 for selection in &mut selections {
3067 if selection.start.row != prev_edited_row {
3068 row_delta = 0;
3069 }
3070 prev_edited_row = selection.end.row;
3071
3072 row_delta =
3073 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3074 }
3075
3076 self.transact(cx, |this, cx| {
3077 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3078 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3079 });
3080 }
3081
3082 fn indent_selection(
3083 buffer: &MultiBuffer,
3084 snapshot: &MultiBufferSnapshot,
3085 selection: &mut Selection<Point>,
3086 edits: &mut Vec<(Range<Point>, String)>,
3087 delta_for_start_row: u32,
3088 cx: &AppContext,
3089 ) -> u32 {
3090 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3091 let settings = cx.global::<Settings>();
3092 let tab_size = settings.tab_size(language_name.as_deref()).get();
3093 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3094 IndentKind::Tab
3095 } else {
3096 IndentKind::Space
3097 };
3098 let mut start_row = selection.start.row;
3099 let mut end_row = selection.end.row + 1;
3100
3101 // If a selection ends at the beginning of a line, don't indent
3102 // that last line.
3103 if selection.end.column == 0 {
3104 end_row -= 1;
3105 }
3106
3107 // Avoid re-indenting a row that has already been indented by a
3108 // previous selection, but still update this selection's column
3109 // to reflect that indentation.
3110 if delta_for_start_row > 0 {
3111 start_row += 1;
3112 selection.start.column += delta_for_start_row;
3113 if selection.end.row == selection.start.row {
3114 selection.end.column += delta_for_start_row;
3115 }
3116 }
3117
3118 let mut delta_for_end_row = 0;
3119 for row in start_row..end_row {
3120 let current_indent = snapshot.indent_size_for_line(row);
3121 let indent_delta = match (current_indent.kind, indent_kind) {
3122 (IndentKind::Space, IndentKind::Space) => {
3123 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3124 IndentSize::spaces(columns_to_next_tab_stop)
3125 }
3126 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3127 (_, IndentKind::Tab) => IndentSize::tab(),
3128 };
3129
3130 let row_start = Point::new(row, 0);
3131 edits.push((
3132 row_start..row_start,
3133 indent_delta.chars().collect::<String>(),
3134 ));
3135
3136 // Update this selection's endpoints to reflect the indentation.
3137 if row == selection.start.row {
3138 selection.start.column += indent_delta.len;
3139 }
3140 if row == selection.end.row {
3141 selection.end.column += indent_delta.len;
3142 delta_for_end_row = indent_delta.len;
3143 }
3144 }
3145
3146 if selection.start.row == selection.end.row {
3147 delta_for_start_row + delta_for_end_row
3148 } else {
3149 delta_for_end_row
3150 }
3151 }
3152
3153 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3154 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3155 let selections = self.selections.all::<Point>(cx);
3156 let mut deletion_ranges = Vec::new();
3157 let mut last_outdent = None;
3158 {
3159 let buffer = self.buffer.read(cx);
3160 let snapshot = buffer.snapshot(cx);
3161 for selection in &selections {
3162 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3163 let tab_size = cx
3164 .global::<Settings>()
3165 .tab_size(language_name.as_deref())
3166 .get();
3167 let mut rows = selection.spanned_rows(false, &display_map);
3168
3169 // Avoid re-outdenting a row that has already been outdented by a
3170 // previous selection.
3171 if let Some(last_row) = last_outdent {
3172 if last_row == rows.start {
3173 rows.start += 1;
3174 }
3175 }
3176
3177 for row in rows {
3178 let indent_size = snapshot.indent_size_for_line(row);
3179 if indent_size.len > 0 {
3180 let deletion_len = match indent_size.kind {
3181 IndentKind::Space => {
3182 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3183 if columns_to_prev_tab_stop == 0 {
3184 tab_size
3185 } else {
3186 columns_to_prev_tab_stop
3187 }
3188 }
3189 IndentKind::Tab => 1,
3190 };
3191 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3192 last_outdent = Some(row);
3193 }
3194 }
3195 }
3196 }
3197
3198 self.transact(cx, |this, cx| {
3199 this.buffer.update(cx, |buffer, cx| {
3200 let empty_str: Arc<str> = "".into();
3201 buffer.edit(
3202 deletion_ranges
3203 .into_iter()
3204 .map(|range| (range, empty_str.clone())),
3205 None,
3206 cx,
3207 );
3208 });
3209 let selections = this.selections.all::<usize>(cx);
3210 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3211 });
3212 }
3213
3214 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3215 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3216 let selections = self.selections.all::<Point>(cx);
3217
3218 let mut new_cursors = Vec::new();
3219 let mut edit_ranges = Vec::new();
3220 let mut selections = selections.iter().peekable();
3221 while let Some(selection) = selections.next() {
3222 let mut rows = selection.spanned_rows(false, &display_map);
3223 let goal_display_column = selection.head().to_display_point(&display_map).column();
3224
3225 // Accumulate contiguous regions of rows that we want to delete.
3226 while let Some(next_selection) = selections.peek() {
3227 let next_rows = next_selection.spanned_rows(false, &display_map);
3228 if next_rows.start <= rows.end {
3229 rows.end = next_rows.end;
3230 selections.next().unwrap();
3231 } else {
3232 break;
3233 }
3234 }
3235
3236 let buffer = &display_map.buffer_snapshot;
3237 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3238 let edit_end;
3239 let cursor_buffer_row;
3240 if buffer.max_point().row >= rows.end {
3241 // If there's a line after the range, delete the \n from the end of the row range
3242 // and position the cursor on the next line.
3243 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3244 cursor_buffer_row = rows.end;
3245 } else {
3246 // If there isn't a line after the range, delete the \n from the line before the
3247 // start of the row range and position the cursor there.
3248 edit_start = edit_start.saturating_sub(1);
3249 edit_end = buffer.len();
3250 cursor_buffer_row = rows.start.saturating_sub(1);
3251 }
3252
3253 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3254 *cursor.column_mut() =
3255 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3256
3257 new_cursors.push((
3258 selection.id,
3259 buffer.anchor_after(cursor.to_point(&display_map)),
3260 ));
3261 edit_ranges.push(edit_start..edit_end);
3262 }
3263
3264 self.transact(cx, |this, cx| {
3265 let buffer = this.buffer.update(cx, |buffer, cx| {
3266 let empty_str: Arc<str> = "".into();
3267 buffer.edit(
3268 edit_ranges
3269 .into_iter()
3270 .map(|range| (range, empty_str.clone())),
3271 None,
3272 cx,
3273 );
3274 buffer.snapshot(cx)
3275 });
3276 let new_selections = new_cursors
3277 .into_iter()
3278 .map(|(id, cursor)| {
3279 let cursor = cursor.to_point(&buffer);
3280 Selection {
3281 id,
3282 start: cursor,
3283 end: cursor,
3284 reversed: false,
3285 goal: SelectionGoal::None,
3286 }
3287 })
3288 .collect();
3289
3290 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3291 s.select(new_selections);
3292 });
3293 });
3294 }
3295
3296 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3298 let buffer = &display_map.buffer_snapshot;
3299 let selections = self.selections.all::<Point>(cx);
3300
3301 let mut edits = Vec::new();
3302 let mut selections_iter = selections.iter().peekable();
3303 while let Some(selection) = selections_iter.next() {
3304 // Avoid duplicating the same lines twice.
3305 let mut rows = selection.spanned_rows(false, &display_map);
3306
3307 while let Some(next_selection) = selections_iter.peek() {
3308 let next_rows = next_selection.spanned_rows(false, &display_map);
3309 if next_rows.start < rows.end {
3310 rows.end = next_rows.end;
3311 selections_iter.next().unwrap();
3312 } else {
3313 break;
3314 }
3315 }
3316
3317 // Copy the text from the selected row region and splice it at the start of the region.
3318 let start = Point::new(rows.start, 0);
3319 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3320 let text = buffer
3321 .text_for_range(start..end)
3322 .chain(Some("\n"))
3323 .collect::<String>();
3324 edits.push((start..start, text));
3325 }
3326
3327 self.transact(cx, |this, cx| {
3328 this.buffer.update(cx, |buffer, cx| {
3329 buffer.edit(edits, None, cx);
3330 });
3331
3332 this.request_autoscroll(Autoscroll::Fit, cx);
3333 });
3334 }
3335
3336 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3337 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3338 let buffer = self.buffer.read(cx).snapshot(cx);
3339
3340 let mut edits = Vec::new();
3341 let mut unfold_ranges = Vec::new();
3342 let mut refold_ranges = Vec::new();
3343
3344 let selections = self.selections.all::<Point>(cx);
3345 let mut selections = selections.iter().peekable();
3346 let mut contiguous_row_selections = Vec::new();
3347 let mut new_selections = Vec::new();
3348
3349 while let Some(selection) = selections.next() {
3350 // Find all the selections that span a contiguous row range
3351 contiguous_row_selections.push(selection.clone());
3352 let start_row = selection.start.row;
3353 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3354 display_map.next_line_boundary(selection.end).0.row + 1
3355 } else {
3356 selection.end.row
3357 };
3358
3359 while let Some(next_selection) = selections.peek() {
3360 if next_selection.start.row <= end_row {
3361 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3362 display_map.next_line_boundary(next_selection.end).0.row + 1
3363 } else {
3364 next_selection.end.row
3365 };
3366 contiguous_row_selections.push(selections.next().unwrap().clone());
3367 } else {
3368 break;
3369 }
3370 }
3371
3372 // Move the text spanned by the row range to be before the line preceding the row range
3373 if start_row > 0 {
3374 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3375 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3376 let insertion_point = display_map
3377 .prev_line_boundary(Point::new(start_row - 1, 0))
3378 .0;
3379
3380 // Don't move lines across excerpts
3381 if buffer
3382 .excerpt_boundaries_in_range((
3383 Bound::Excluded(insertion_point),
3384 Bound::Included(range_to_move.end),
3385 ))
3386 .next()
3387 .is_none()
3388 {
3389 let text = buffer
3390 .text_for_range(range_to_move.clone())
3391 .flat_map(|s| s.chars())
3392 .skip(1)
3393 .chain(['\n'])
3394 .collect::<String>();
3395
3396 edits.push((
3397 buffer.anchor_after(range_to_move.start)
3398 ..buffer.anchor_before(range_to_move.end),
3399 String::new(),
3400 ));
3401 let insertion_anchor = buffer.anchor_after(insertion_point);
3402 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3403
3404 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3405
3406 // Move selections up
3407 new_selections.extend(contiguous_row_selections.drain(..).map(
3408 |mut selection| {
3409 selection.start.row -= row_delta;
3410 selection.end.row -= row_delta;
3411 selection
3412 },
3413 ));
3414
3415 // Move folds up
3416 unfold_ranges.push(range_to_move.clone());
3417 for fold in display_map.folds_in_range(
3418 buffer.anchor_before(range_to_move.start)
3419 ..buffer.anchor_after(range_to_move.end),
3420 ) {
3421 let mut start = fold.start.to_point(&buffer);
3422 let mut end = fold.end.to_point(&buffer);
3423 start.row -= row_delta;
3424 end.row -= row_delta;
3425 refold_ranges.push(start..end);
3426 }
3427 }
3428 }
3429
3430 // If we didn't move line(s), preserve the existing selections
3431 new_selections.append(&mut contiguous_row_selections);
3432 }
3433
3434 self.transact(cx, |this, cx| {
3435 this.unfold_ranges(unfold_ranges, true, cx);
3436 this.buffer.update(cx, |buffer, cx| {
3437 for (range, text) in edits {
3438 buffer.edit([(range, text)], None, cx);
3439 }
3440 });
3441 this.fold_ranges(refold_ranges, cx);
3442 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3443 s.select(new_selections);
3444 })
3445 });
3446 }
3447
3448 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3449 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3450 let buffer = self.buffer.read(cx).snapshot(cx);
3451
3452 let mut edits = Vec::new();
3453 let mut unfold_ranges = Vec::new();
3454 let mut refold_ranges = Vec::new();
3455
3456 let selections = self.selections.all::<Point>(cx);
3457 let mut selections = selections.iter().peekable();
3458 let mut contiguous_row_selections = Vec::new();
3459 let mut new_selections = Vec::new();
3460
3461 while let Some(selection) = selections.next() {
3462 // Find all the selections that span a contiguous row range
3463 contiguous_row_selections.push(selection.clone());
3464 let start_row = selection.start.row;
3465 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3466 display_map.next_line_boundary(selection.end).0.row + 1
3467 } else {
3468 selection.end.row
3469 };
3470
3471 while let Some(next_selection) = selections.peek() {
3472 if next_selection.start.row <= end_row {
3473 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3474 display_map.next_line_boundary(next_selection.end).0.row + 1
3475 } else {
3476 next_selection.end.row
3477 };
3478 contiguous_row_selections.push(selections.next().unwrap().clone());
3479 } else {
3480 break;
3481 }
3482 }
3483
3484 // Move the text spanned by the row range to be after the last line of the row range
3485 if end_row <= buffer.max_point().row {
3486 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3487 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3488
3489 // Don't move lines across excerpt boundaries
3490 if buffer
3491 .excerpt_boundaries_in_range((
3492 Bound::Excluded(range_to_move.start),
3493 Bound::Included(insertion_point),
3494 ))
3495 .next()
3496 .is_none()
3497 {
3498 let mut text = String::from("\n");
3499 text.extend(buffer.text_for_range(range_to_move.clone()));
3500 text.pop(); // Drop trailing newline
3501 edits.push((
3502 buffer.anchor_after(range_to_move.start)
3503 ..buffer.anchor_before(range_to_move.end),
3504 String::new(),
3505 ));
3506 let insertion_anchor = buffer.anchor_after(insertion_point);
3507 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3508
3509 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3510
3511 // Move selections down
3512 new_selections.extend(contiguous_row_selections.drain(..).map(
3513 |mut selection| {
3514 selection.start.row += row_delta;
3515 selection.end.row += row_delta;
3516 selection
3517 },
3518 ));
3519
3520 // Move folds down
3521 unfold_ranges.push(range_to_move.clone());
3522 for fold in display_map.folds_in_range(
3523 buffer.anchor_before(range_to_move.start)
3524 ..buffer.anchor_after(range_to_move.end),
3525 ) {
3526 let mut start = fold.start.to_point(&buffer);
3527 let mut end = fold.end.to_point(&buffer);
3528 start.row += row_delta;
3529 end.row += row_delta;
3530 refold_ranges.push(start..end);
3531 }
3532 }
3533 }
3534
3535 // If we didn't move line(s), preserve the existing selections
3536 new_selections.append(&mut contiguous_row_selections);
3537 }
3538
3539 self.transact(cx, |this, cx| {
3540 this.unfold_ranges(unfold_ranges, true, cx);
3541 this.buffer.update(cx, |buffer, cx| {
3542 for (range, text) in edits {
3543 buffer.edit([(range, text)], None, cx);
3544 }
3545 });
3546 this.fold_ranges(refold_ranges, cx);
3547 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3548 });
3549 }
3550
3551 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3552 self.transact(cx, |this, cx| {
3553 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3554 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3555 let line_mode = s.line_mode;
3556 s.move_with(|display_map, selection| {
3557 if !selection.is_empty() || line_mode {
3558 return;
3559 }
3560
3561 let mut head = selection.head();
3562 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3563 if head.column() == display_map.line_len(head.row()) {
3564 transpose_offset = display_map
3565 .buffer_snapshot
3566 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3567 }
3568
3569 if transpose_offset == 0 {
3570 return;
3571 }
3572
3573 *head.column_mut() += 1;
3574 head = display_map.clip_point(head, Bias::Right);
3575 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3576
3577 let transpose_start = display_map
3578 .buffer_snapshot
3579 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3580 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3581 let transpose_end = display_map
3582 .buffer_snapshot
3583 .clip_offset(transpose_offset + 1, Bias::Right);
3584 if let Some(ch) =
3585 display_map.buffer_snapshot.chars_at(transpose_start).next()
3586 {
3587 edits.push((transpose_start..transpose_offset, String::new()));
3588 edits.push((transpose_end..transpose_end, ch.to_string()));
3589 }
3590 }
3591 });
3592 edits
3593 });
3594 this.buffer
3595 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3596 let selections = this.selections.all::<usize>(cx);
3597 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3598 s.select(selections);
3599 });
3600 });
3601 }
3602
3603 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3604 let mut text = String::new();
3605 let buffer = self.buffer.read(cx).snapshot(cx);
3606 let mut selections = self.selections.all::<Point>(cx);
3607 let mut clipboard_selections = Vec::with_capacity(selections.len());
3608 {
3609 let max_point = buffer.max_point();
3610 for selection in &mut selections {
3611 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3612 if is_entire_line {
3613 selection.start = Point::new(selection.start.row, 0);
3614 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3615 selection.goal = SelectionGoal::None;
3616 }
3617 let mut len = 0;
3618 for chunk in buffer.text_for_range(selection.start..selection.end) {
3619 text.push_str(chunk);
3620 len += chunk.len();
3621 }
3622 clipboard_selections.push(ClipboardSelection {
3623 len,
3624 is_entire_line,
3625 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3626 });
3627 }
3628 }
3629
3630 self.transact(cx, |this, cx| {
3631 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3632 s.select(selections);
3633 });
3634 this.insert("", cx);
3635 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3636 });
3637 }
3638
3639 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3640 let selections = self.selections.all::<Point>(cx);
3641 let buffer = self.buffer.read(cx).read(cx);
3642 let mut text = String::new();
3643
3644 let mut clipboard_selections = Vec::with_capacity(selections.len());
3645 {
3646 let max_point = buffer.max_point();
3647 for selection in selections.iter() {
3648 let mut start = selection.start;
3649 let mut end = selection.end;
3650 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3651 if is_entire_line {
3652 start = Point::new(start.row, 0);
3653 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3654 }
3655 let mut len = 0;
3656 for chunk in buffer.text_for_range(start..end) {
3657 text.push_str(chunk);
3658 len += chunk.len();
3659 }
3660 clipboard_selections.push(ClipboardSelection {
3661 len,
3662 is_entire_line,
3663 first_line_indent: buffer.indent_size_for_line(start.row).len,
3664 });
3665 }
3666 }
3667
3668 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3669 }
3670
3671 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3672 self.transact(cx, |this, cx| {
3673 if let Some(item) = cx.as_mut().read_from_clipboard() {
3674 let mut clipboard_text = Cow::Borrowed(item.text());
3675 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3676 let old_selections = this.selections.all::<usize>(cx);
3677 let all_selections_were_entire_line =
3678 clipboard_selections.iter().all(|s| s.is_entire_line);
3679 let first_selection_indent_column =
3680 clipboard_selections.first().map(|s| s.first_line_indent);
3681 if clipboard_selections.len() != old_selections.len() {
3682 let mut newline_separated_text = String::new();
3683 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3684 let mut ix = 0;
3685 while let Some(clipboard_selection) = clipboard_selections.next() {
3686 newline_separated_text
3687 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3688 ix += clipboard_selection.len;
3689 if clipboard_selections.peek().is_some() {
3690 newline_separated_text.push('\n');
3691 }
3692 }
3693 clipboard_text = Cow::Owned(newline_separated_text);
3694 }
3695
3696 this.buffer.update(cx, |buffer, cx| {
3697 let snapshot = buffer.read(cx);
3698 let mut start_offset = 0;
3699 let mut edits = Vec::new();
3700 let mut original_indent_columns = Vec::new();
3701 let line_mode = this.selections.line_mode;
3702 for (ix, selection) in old_selections.iter().enumerate() {
3703 let to_insert;
3704 let entire_line;
3705 let original_indent_column;
3706 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3707 let end_offset = start_offset + clipboard_selection.len;
3708 to_insert = &clipboard_text[start_offset..end_offset];
3709 entire_line = clipboard_selection.is_entire_line;
3710 start_offset = end_offset;
3711 original_indent_column =
3712 Some(clipboard_selection.first_line_indent);
3713 } else {
3714 to_insert = clipboard_text.as_str();
3715 entire_line = all_selections_were_entire_line;
3716 original_indent_column = first_selection_indent_column
3717 }
3718
3719 // If the corresponding selection was empty when this slice of the
3720 // clipboard text was written, then the entire line containing the
3721 // selection was copied. If this selection is also currently empty,
3722 // then paste the line before the current line of the buffer.
3723 let range = if selection.is_empty() && !line_mode && entire_line {
3724 let column = selection.start.to_point(&snapshot).column as usize;
3725 let line_start = selection.start - column;
3726 line_start..line_start
3727 } else {
3728 selection.range()
3729 };
3730
3731 edits.push((range, to_insert));
3732 original_indent_columns.extend(original_indent_column);
3733 }
3734 drop(snapshot);
3735
3736 buffer.edit(
3737 edits,
3738 Some(AutoindentMode::Block {
3739 original_indent_columns,
3740 }),
3741 cx,
3742 );
3743 });
3744
3745 let selections = this.selections.all::<usize>(cx);
3746 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3747 } else {
3748 this.insert(&clipboard_text, cx);
3749 }
3750 }
3751 });
3752 }
3753
3754 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3755 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3756 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3757 self.change_selections(None, cx, |s| {
3758 s.select_anchors(selections.to_vec());
3759 });
3760 }
3761 self.request_autoscroll(Autoscroll::Fit, cx);
3762 self.unmark_text(cx);
3763 cx.emit(Event::Edited);
3764 }
3765 }
3766
3767 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3768 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3769 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3770 {
3771 self.change_selections(None, cx, |s| {
3772 s.select_anchors(selections.to_vec());
3773 });
3774 }
3775 self.request_autoscroll(Autoscroll::Fit, cx);
3776 self.unmark_text(cx);
3777 cx.emit(Event::Edited);
3778 }
3779 }
3780
3781 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3782 self.buffer
3783 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3784 }
3785
3786 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3787 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3788 let line_mode = s.line_mode;
3789 s.move_with(|map, selection| {
3790 let cursor = if selection.is_empty() && !line_mode {
3791 movement::left(map, selection.start)
3792 } else {
3793 selection.start
3794 };
3795 selection.collapse_to(cursor, SelectionGoal::None);
3796 });
3797 })
3798 }
3799
3800 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3801 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3802 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3803 })
3804 }
3805
3806 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3807 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3808 let line_mode = s.line_mode;
3809 s.move_with(|map, selection| {
3810 let cursor = if selection.is_empty() && !line_mode {
3811 movement::right(map, selection.end)
3812 } else {
3813 selection.end
3814 };
3815 selection.collapse_to(cursor, SelectionGoal::None)
3816 });
3817 })
3818 }
3819
3820 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3821 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3822 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3823 })
3824 }
3825
3826 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3827 if self.take_rename(true, cx).is_some() {
3828 return;
3829 }
3830
3831 if let Some(context_menu) = self.context_menu.as_mut() {
3832 if context_menu.select_prev(cx) {
3833 return;
3834 }
3835 }
3836
3837 if matches!(self.mode, EditorMode::SingleLine) {
3838 cx.propagate_action();
3839 return;
3840 }
3841
3842 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3843 let line_mode = s.line_mode;
3844 s.move_with(|map, selection| {
3845 if !selection.is_empty() && !line_mode {
3846 selection.goal = SelectionGoal::None;
3847 }
3848 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
3849 selection.collapse_to(cursor, goal);
3850 });
3851 })
3852 }
3853
3854 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3855 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3856 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3857 })
3858 }
3859
3860 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3861 self.take_rename(true, cx);
3862
3863 if let Some(context_menu) = self.context_menu.as_mut() {
3864 if context_menu.select_next(cx) {
3865 return;
3866 }
3867 }
3868
3869 if matches!(self.mode, EditorMode::SingleLine) {
3870 cx.propagate_action();
3871 return;
3872 }
3873
3874 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3875 let line_mode = s.line_mode;
3876 s.move_with(|map, selection| {
3877 if !selection.is_empty() && !line_mode {
3878 selection.goal = SelectionGoal::None;
3879 }
3880 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
3881 selection.collapse_to(cursor, goal);
3882 });
3883 });
3884 }
3885
3886 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3887 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3888 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3889 });
3890 }
3891
3892 pub fn move_to_previous_word_start(
3893 &mut self,
3894 _: &MoveToPreviousWordStart,
3895 cx: &mut ViewContext<Self>,
3896 ) {
3897 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3898 s.move_cursors_with(|map, head, _| {
3899 (
3900 movement::previous_word_start(map, head),
3901 SelectionGoal::None,
3902 )
3903 });
3904 })
3905 }
3906
3907 pub fn move_to_previous_subword_start(
3908 &mut self,
3909 _: &MoveToPreviousSubwordStart,
3910 cx: &mut ViewContext<Self>,
3911 ) {
3912 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3913 s.move_cursors_with(|map, head, _| {
3914 (
3915 movement::previous_subword_start(map, head),
3916 SelectionGoal::None,
3917 )
3918 });
3919 })
3920 }
3921
3922 pub fn select_to_previous_word_start(
3923 &mut self,
3924 _: &SelectToPreviousWordStart,
3925 cx: &mut ViewContext<Self>,
3926 ) {
3927 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3928 s.move_heads_with(|map, head, _| {
3929 (
3930 movement::previous_word_start(map, head),
3931 SelectionGoal::None,
3932 )
3933 });
3934 })
3935 }
3936
3937 pub fn select_to_previous_subword_start(
3938 &mut self,
3939 _: &SelectToPreviousSubwordStart,
3940 cx: &mut ViewContext<Self>,
3941 ) {
3942 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3943 s.move_heads_with(|map, head, _| {
3944 (
3945 movement::previous_subword_start(map, head),
3946 SelectionGoal::None,
3947 )
3948 });
3949 })
3950 }
3951
3952 pub fn delete_to_previous_word_start(
3953 &mut self,
3954 _: &DeleteToPreviousWordStart,
3955 cx: &mut ViewContext<Self>,
3956 ) {
3957 self.transact(cx, |this, cx| {
3958 this.select_autoclose_pair(cx);
3959 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3960 let line_mode = s.line_mode;
3961 s.move_with(|map, selection| {
3962 if selection.is_empty() && !line_mode {
3963 let cursor = movement::previous_word_start(map, selection.head());
3964 selection.set_head(cursor, SelectionGoal::None);
3965 }
3966 });
3967 });
3968 this.insert("", cx);
3969 });
3970 }
3971
3972 pub fn delete_to_previous_subword_start(
3973 &mut self,
3974 _: &DeleteToPreviousSubwordStart,
3975 cx: &mut ViewContext<Self>,
3976 ) {
3977 self.transact(cx, |this, cx| {
3978 this.select_autoclose_pair(cx);
3979 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3980 let line_mode = s.line_mode;
3981 s.move_with(|map, selection| {
3982 if selection.is_empty() && !line_mode {
3983 let cursor = movement::previous_subword_start(map, selection.head());
3984 selection.set_head(cursor, SelectionGoal::None);
3985 }
3986 });
3987 });
3988 this.insert("", cx);
3989 });
3990 }
3991
3992 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3993 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3994 s.move_cursors_with(|map, head, _| {
3995 (movement::next_word_end(map, head), SelectionGoal::None)
3996 });
3997 })
3998 }
3999
4000 pub fn move_to_next_subword_end(
4001 &mut self,
4002 _: &MoveToNextSubwordEnd,
4003 cx: &mut ViewContext<Self>,
4004 ) {
4005 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4006 s.move_cursors_with(|map, head, _| {
4007 (movement::next_subword_end(map, head), SelectionGoal::None)
4008 });
4009 })
4010 }
4011
4012 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4013 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4014 s.move_heads_with(|map, head, _| {
4015 (movement::next_word_end(map, head), SelectionGoal::None)
4016 });
4017 })
4018 }
4019
4020 pub fn select_to_next_subword_end(
4021 &mut self,
4022 _: &SelectToNextSubwordEnd,
4023 cx: &mut ViewContext<Self>,
4024 ) {
4025 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4026 s.move_heads_with(|map, head, _| {
4027 (movement::next_subword_end(map, head), SelectionGoal::None)
4028 });
4029 })
4030 }
4031
4032 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4033 self.transact(cx, |this, cx| {
4034 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4035 let line_mode = s.line_mode;
4036 s.move_with(|map, selection| {
4037 if selection.is_empty() && !line_mode {
4038 let cursor = movement::next_word_end(map, selection.head());
4039 selection.set_head(cursor, SelectionGoal::None);
4040 }
4041 });
4042 });
4043 this.insert("", cx);
4044 });
4045 }
4046
4047 pub fn delete_to_next_subword_end(
4048 &mut self,
4049 _: &DeleteToNextSubwordEnd,
4050 cx: &mut ViewContext<Self>,
4051 ) {
4052 self.transact(cx, |this, cx| {
4053 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4054 s.move_with(|map, selection| {
4055 if selection.is_empty() {
4056 let cursor = movement::next_subword_end(map, selection.head());
4057 selection.set_head(cursor, SelectionGoal::None);
4058 }
4059 });
4060 });
4061 this.insert("", cx);
4062 });
4063 }
4064
4065 pub fn move_to_beginning_of_line(
4066 &mut self,
4067 _: &MoveToBeginningOfLine,
4068 cx: &mut ViewContext<Self>,
4069 ) {
4070 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4071 s.move_cursors_with(|map, head, _| {
4072 (
4073 movement::line_beginning(map, head, true),
4074 SelectionGoal::None,
4075 )
4076 });
4077 })
4078 }
4079
4080 pub fn select_to_beginning_of_line(
4081 &mut self,
4082 action: &SelectToBeginningOfLine,
4083 cx: &mut ViewContext<Self>,
4084 ) {
4085 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4086 s.move_heads_with(|map, head, _| {
4087 (
4088 movement::line_beginning(map, head, action.stop_at_soft_wraps),
4089 SelectionGoal::None,
4090 )
4091 });
4092 });
4093 }
4094
4095 pub fn delete_to_beginning_of_line(
4096 &mut self,
4097 _: &DeleteToBeginningOfLine,
4098 cx: &mut ViewContext<Self>,
4099 ) {
4100 self.transact(cx, |this, cx| {
4101 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4102 s.move_with(|_, selection| {
4103 selection.reversed = true;
4104 });
4105 });
4106
4107 this.select_to_beginning_of_line(
4108 &SelectToBeginningOfLine {
4109 stop_at_soft_wraps: false,
4110 },
4111 cx,
4112 );
4113 this.backspace(&Backspace, cx);
4114 });
4115 }
4116
4117 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4118 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4119 s.move_cursors_with(|map, head, _| {
4120 (movement::line_end(map, head, true), SelectionGoal::None)
4121 });
4122 })
4123 }
4124
4125 pub fn select_to_end_of_line(
4126 &mut self,
4127 action: &SelectToEndOfLine,
4128 cx: &mut ViewContext<Self>,
4129 ) {
4130 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4131 s.move_heads_with(|map, head, _| {
4132 (
4133 movement::line_end(map, head, action.stop_at_soft_wraps),
4134 SelectionGoal::None,
4135 )
4136 });
4137 })
4138 }
4139
4140 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4141 self.transact(cx, |this, cx| {
4142 this.select_to_end_of_line(
4143 &SelectToEndOfLine {
4144 stop_at_soft_wraps: false,
4145 },
4146 cx,
4147 );
4148 this.delete(&Delete, cx);
4149 });
4150 }
4151
4152 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4153 self.transact(cx, |this, cx| {
4154 this.select_to_end_of_line(
4155 &SelectToEndOfLine {
4156 stop_at_soft_wraps: false,
4157 },
4158 cx,
4159 );
4160 this.cut(&Cut, cx);
4161 });
4162 }
4163
4164 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4165 if matches!(self.mode, EditorMode::SingleLine) {
4166 cx.propagate_action();
4167 return;
4168 }
4169
4170 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4171 s.select_ranges(vec![0..0]);
4172 });
4173 }
4174
4175 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4176 let mut selection = self.selections.last::<Point>(cx);
4177 selection.set_head(Point::zero(), SelectionGoal::None);
4178
4179 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4180 s.select(vec![selection]);
4181 });
4182 }
4183
4184 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4185 if matches!(self.mode, EditorMode::SingleLine) {
4186 cx.propagate_action();
4187 return;
4188 }
4189
4190 let cursor = self.buffer.read(cx).read(cx).len();
4191 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4192 s.select_ranges(vec![cursor..cursor])
4193 });
4194 }
4195
4196 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4197 self.nav_history = nav_history;
4198 }
4199
4200 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4201 self.nav_history.as_ref()
4202 }
4203
4204 fn push_to_nav_history(
4205 &self,
4206 position: Anchor,
4207 new_position: Option<Point>,
4208 cx: &mut ViewContext<Self>,
4209 ) {
4210 if let Some(nav_history) = &self.nav_history {
4211 let buffer = self.buffer.read(cx).read(cx);
4212 let point = position.to_point(&buffer);
4213 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4214 drop(buffer);
4215
4216 if let Some(new_position) = new_position {
4217 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4218 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4219 return;
4220 }
4221 }
4222
4223 nav_history.push(
4224 Some(NavigationData {
4225 cursor_anchor: position,
4226 cursor_position: point,
4227 scroll_position: self.scroll_position,
4228 scroll_top_anchor: self.scroll_top_anchor.clone(),
4229 scroll_top_row,
4230 }),
4231 cx,
4232 );
4233 }
4234 }
4235
4236 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4237 let buffer = self.buffer.read(cx).snapshot(cx);
4238 let mut selection = self.selections.first::<usize>(cx);
4239 selection.set_head(buffer.len(), SelectionGoal::None);
4240 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4241 s.select(vec![selection]);
4242 });
4243 }
4244
4245 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4246 let end = self.buffer.read(cx).read(cx).len();
4247 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4248 s.select_ranges(vec![0..end]);
4249 });
4250 }
4251
4252 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4253 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4254 let mut selections = self.selections.all::<Point>(cx);
4255 let max_point = display_map.buffer_snapshot.max_point();
4256 for selection in &mut selections {
4257 let rows = selection.spanned_rows(true, &display_map);
4258 selection.start = Point::new(rows.start, 0);
4259 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4260 selection.reversed = false;
4261 }
4262 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4263 s.select(selections);
4264 });
4265 }
4266
4267 pub fn split_selection_into_lines(
4268 &mut self,
4269 _: &SplitSelectionIntoLines,
4270 cx: &mut ViewContext<Self>,
4271 ) {
4272 let mut to_unfold = Vec::new();
4273 let mut new_selection_ranges = Vec::new();
4274 {
4275 let selections = self.selections.all::<Point>(cx);
4276 let buffer = self.buffer.read(cx).read(cx);
4277 for selection in selections {
4278 for row in selection.start.row..selection.end.row {
4279 let cursor = Point::new(row, buffer.line_len(row));
4280 new_selection_ranges.push(cursor..cursor);
4281 }
4282 new_selection_ranges.push(selection.end..selection.end);
4283 to_unfold.push(selection.start..selection.end);
4284 }
4285 }
4286 self.unfold_ranges(to_unfold, true, cx);
4287 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4288 s.select_ranges(new_selection_ranges);
4289 });
4290 }
4291
4292 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4293 self.add_selection(true, cx);
4294 }
4295
4296 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4297 self.add_selection(false, cx);
4298 }
4299
4300 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4301 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4302 let mut selections = self.selections.all::<Point>(cx);
4303 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4304 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4305 let range = oldest_selection.display_range(&display_map).sorted();
4306 let columns = cmp::min(range.start.column(), range.end.column())
4307 ..cmp::max(range.start.column(), range.end.column());
4308
4309 selections.clear();
4310 let mut stack = Vec::new();
4311 for row in range.start.row()..=range.end.row() {
4312 if let Some(selection) = self.selections.build_columnar_selection(
4313 &display_map,
4314 row,
4315 &columns,
4316 oldest_selection.reversed,
4317 ) {
4318 stack.push(selection.id);
4319 selections.push(selection);
4320 }
4321 }
4322
4323 if above {
4324 stack.reverse();
4325 }
4326
4327 AddSelectionsState { above, stack }
4328 });
4329
4330 let last_added_selection = *state.stack.last().unwrap();
4331 let mut new_selections = Vec::new();
4332 if above == state.above {
4333 let end_row = if above {
4334 0
4335 } else {
4336 display_map.max_point().row()
4337 };
4338
4339 'outer: for selection in selections {
4340 if selection.id == last_added_selection {
4341 let range = selection.display_range(&display_map).sorted();
4342 debug_assert_eq!(range.start.row(), range.end.row());
4343 let mut row = range.start.row();
4344 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4345 {
4346 start..end
4347 } else {
4348 cmp::min(range.start.column(), range.end.column())
4349 ..cmp::max(range.start.column(), range.end.column())
4350 };
4351
4352 while row != end_row {
4353 if above {
4354 row -= 1;
4355 } else {
4356 row += 1;
4357 }
4358
4359 if let Some(new_selection) = self.selections.build_columnar_selection(
4360 &display_map,
4361 row,
4362 &columns,
4363 selection.reversed,
4364 ) {
4365 state.stack.push(new_selection.id);
4366 if above {
4367 new_selections.push(new_selection);
4368 new_selections.push(selection);
4369 } else {
4370 new_selections.push(selection);
4371 new_selections.push(new_selection);
4372 }
4373
4374 continue 'outer;
4375 }
4376 }
4377 }
4378
4379 new_selections.push(selection);
4380 }
4381 } else {
4382 new_selections = selections;
4383 new_selections.retain(|s| s.id != last_added_selection);
4384 state.stack.pop();
4385 }
4386
4387 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4388 s.select(new_selections);
4389 });
4390 if state.stack.len() > 1 {
4391 self.add_selections_state = Some(state);
4392 }
4393 }
4394
4395 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4396 self.push_to_selection_history();
4397 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4398 let buffer = &display_map.buffer_snapshot;
4399 let mut selections = self.selections.all::<usize>(cx);
4400 if let Some(mut select_next_state) = self.select_next_state.take() {
4401 let query = &select_next_state.query;
4402 if !select_next_state.done {
4403 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4404 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4405 let mut next_selected_range = None;
4406
4407 let bytes_after_last_selection =
4408 buffer.bytes_in_range(last_selection.end..buffer.len());
4409 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4410 let query_matches = query
4411 .stream_find_iter(bytes_after_last_selection)
4412 .map(|result| (last_selection.end, result))
4413 .chain(
4414 query
4415 .stream_find_iter(bytes_before_first_selection)
4416 .map(|result| (0, result)),
4417 );
4418 for (start_offset, query_match) in query_matches {
4419 let query_match = query_match.unwrap(); // can only fail due to I/O
4420 let offset_range =
4421 start_offset + query_match.start()..start_offset + query_match.end();
4422 let display_range = offset_range.start.to_display_point(&display_map)
4423 ..offset_range.end.to_display_point(&display_map);
4424
4425 if !select_next_state.wordwise
4426 || (!movement::is_inside_word(&display_map, display_range.start)
4427 && !movement::is_inside_word(&display_map, display_range.end))
4428 {
4429 next_selected_range = Some(offset_range);
4430 break;
4431 }
4432 }
4433
4434 if let Some(next_selected_range) = next_selected_range {
4435 self.unfold_ranges([next_selected_range.clone()], false, cx);
4436 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4437 if action.replace_newest {
4438 s.delete(s.newest_anchor().id);
4439 }
4440 s.insert_range(next_selected_range);
4441 });
4442 } else {
4443 select_next_state.done = true;
4444 }
4445 }
4446
4447 self.select_next_state = Some(select_next_state);
4448 } else if selections.len() == 1 {
4449 let selection = selections.last_mut().unwrap();
4450 if selection.start == selection.end {
4451 let word_range = movement::surrounding_word(
4452 &display_map,
4453 selection.start.to_display_point(&display_map),
4454 );
4455 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4456 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4457 selection.goal = SelectionGoal::None;
4458 selection.reversed = false;
4459
4460 let query = buffer
4461 .text_for_range(selection.start..selection.end)
4462 .collect::<String>();
4463 let select_state = SelectNextState {
4464 query: AhoCorasick::new_auto_configured(&[query]),
4465 wordwise: true,
4466 done: false,
4467 };
4468 self.unfold_ranges([selection.start..selection.end], false, cx);
4469 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4470 s.select(selections);
4471 });
4472 self.select_next_state = Some(select_state);
4473 } else {
4474 let query = buffer
4475 .text_for_range(selection.start..selection.end)
4476 .collect::<String>();
4477 self.select_next_state = Some(SelectNextState {
4478 query: AhoCorasick::new_auto_configured(&[query]),
4479 wordwise: false,
4480 done: false,
4481 });
4482 self.select_next(action, cx);
4483 }
4484 }
4485 }
4486
4487 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4488 self.transact(cx, |this, cx| {
4489 let mut selections = this.selections.all::<Point>(cx);
4490 let mut edits = Vec::new();
4491 let mut selection_edit_ranges = Vec::new();
4492 let mut last_toggled_row = None;
4493 let snapshot = this.buffer.read(cx).read(cx);
4494 let empty_str: Arc<str> = "".into();
4495 let mut suffixes_inserted = Vec::new();
4496
4497 fn comment_prefix_range(
4498 snapshot: &MultiBufferSnapshot,
4499 row: u32,
4500 comment_prefix: &str,
4501 comment_prefix_whitespace: &str,
4502 ) -> Range<Point> {
4503 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4504
4505 let mut line_bytes = snapshot
4506 .bytes_in_range(start..snapshot.max_point())
4507 .flatten()
4508 .copied();
4509
4510 // If this line currently begins with the line comment prefix, then record
4511 // the range containing the prefix.
4512 if line_bytes
4513 .by_ref()
4514 .take(comment_prefix.len())
4515 .eq(comment_prefix.bytes())
4516 {
4517 // Include any whitespace that matches the comment prefix.
4518 let matching_whitespace_len = line_bytes
4519 .zip(comment_prefix_whitespace.bytes())
4520 .take_while(|(a, b)| a == b)
4521 .count() as u32;
4522 let end = Point::new(
4523 start.row,
4524 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4525 );
4526 start..end
4527 } else {
4528 start..start
4529 }
4530 }
4531
4532 fn comment_suffix_range(
4533 snapshot: &MultiBufferSnapshot,
4534 row: u32,
4535 comment_suffix: &str,
4536 comment_suffix_has_leading_space: bool,
4537 ) -> Range<Point> {
4538 let end = Point::new(row, snapshot.line_len(row));
4539 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4540
4541 let mut line_end_bytes = snapshot
4542 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4543 .flatten()
4544 .copied();
4545
4546 let leading_space_len = if suffix_start_column > 0
4547 && line_end_bytes.next() == Some(b' ')
4548 && comment_suffix_has_leading_space
4549 {
4550 1
4551 } else {
4552 0
4553 };
4554
4555 // If this line currently begins with the line comment prefix, then record
4556 // the range containing the prefix.
4557 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4558 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4559 start..end
4560 } else {
4561 end..end
4562 }
4563 }
4564
4565 // TODO: Handle selections that cross excerpts
4566 for selection in &mut selections {
4567 let language = if let Some(language) = snapshot.language_at(selection.start) {
4568 language
4569 } else {
4570 continue;
4571 };
4572
4573 selection_edit_ranges.clear();
4574
4575 // If multiple selections contain a given row, avoid processing that
4576 // row more than once.
4577 let mut start_row = selection.start.row;
4578 if last_toggled_row == Some(start_row) {
4579 start_row += 1;
4580 }
4581 let end_row =
4582 if selection.end.row > selection.start.row && selection.end.column == 0 {
4583 selection.end.row - 1
4584 } else {
4585 selection.end.row
4586 };
4587 last_toggled_row = Some(end_row);
4588
4589 if start_row > end_row {
4590 continue;
4591 }
4592
4593 // If the language has line comments, toggle those.
4594 if let Some(full_comment_prefix) = language.line_comment_prefix() {
4595 // Split the comment prefix's trailing whitespace into a separate string,
4596 // as that portion won't be used for detecting if a line is a comment.
4597 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4598 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4599 let mut all_selection_lines_are_comments = true;
4600
4601 for row in start_row..=end_row {
4602 if snapshot.is_line_blank(row) {
4603 continue;
4604 }
4605
4606 let prefix_range = comment_prefix_range(
4607 snapshot.deref(),
4608 row,
4609 comment_prefix,
4610 comment_prefix_whitespace,
4611 );
4612 if prefix_range.is_empty() {
4613 all_selection_lines_are_comments = false;
4614 }
4615 selection_edit_ranges.push(prefix_range);
4616 }
4617
4618 if all_selection_lines_are_comments {
4619 edits.extend(
4620 selection_edit_ranges
4621 .iter()
4622 .cloned()
4623 .map(|range| (range, empty_str.clone())),
4624 );
4625 } else {
4626 let min_column = selection_edit_ranges
4627 .iter()
4628 .map(|r| r.start.column)
4629 .min()
4630 .unwrap_or(0);
4631 edits.extend(selection_edit_ranges.iter().map(|range| {
4632 let position = Point::new(range.start.row, min_column);
4633 (position..position, full_comment_prefix.clone())
4634 }));
4635 }
4636 } else if let Some((full_comment_prefix, comment_suffix)) =
4637 language.block_comment_delimiters()
4638 {
4639 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4640 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4641 let prefix_range = comment_prefix_range(
4642 snapshot.deref(),
4643 start_row,
4644 comment_prefix,
4645 comment_prefix_whitespace,
4646 );
4647 let suffix_range = comment_suffix_range(
4648 snapshot.deref(),
4649 end_row,
4650 comment_suffix.trim_start_matches(' '),
4651 comment_suffix.starts_with(' '),
4652 );
4653
4654 if prefix_range.is_empty() || suffix_range.is_empty() {
4655 edits.push((
4656 prefix_range.start..prefix_range.start,
4657 full_comment_prefix.clone(),
4658 ));
4659 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
4660 suffixes_inserted.push((end_row, comment_suffix.len()));
4661 } else {
4662 edits.push((prefix_range, empty_str.clone()));
4663 edits.push((suffix_range, empty_str.clone()));
4664 }
4665 } else {
4666 continue;
4667 }
4668 }
4669
4670 drop(snapshot);
4671 this.buffer.update(cx, |buffer, cx| {
4672 buffer.edit(edits, None, cx);
4673 });
4674
4675 // Adjust selections so that they end before any comment suffixes that
4676 // were inserted.
4677 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
4678 let mut selections = this.selections.all::<Point>(cx);
4679 let snapshot = this.buffer.read(cx).read(cx);
4680 for selection in &mut selections {
4681 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
4682 match row.cmp(&selection.end.row) {
4683 Ordering::Less => {
4684 suffixes_inserted.next();
4685 continue;
4686 }
4687 Ordering::Greater => break,
4688 Ordering::Equal => {
4689 if selection.end.column == snapshot.line_len(row) {
4690 if selection.is_empty() {
4691 selection.start.column -= suffix_len as u32;
4692 }
4693 selection.end.column -= suffix_len as u32;
4694 }
4695 break;
4696 }
4697 }
4698 }
4699 }
4700
4701 drop(snapshot);
4702 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4703 });
4704 }
4705
4706 pub fn select_larger_syntax_node(
4707 &mut self,
4708 _: &SelectLargerSyntaxNode,
4709 cx: &mut ViewContext<Self>,
4710 ) {
4711 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4712 let buffer = self.buffer.read(cx).snapshot(cx);
4713 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4714
4715 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4716 let mut selected_larger_node = false;
4717 let new_selections = old_selections
4718 .iter()
4719 .map(|selection| {
4720 let old_range = selection.start..selection.end;
4721 let mut new_range = old_range.clone();
4722 while let Some(containing_range) =
4723 buffer.range_for_syntax_ancestor(new_range.clone())
4724 {
4725 new_range = containing_range;
4726 if !display_map.intersects_fold(new_range.start)
4727 && !display_map.intersects_fold(new_range.end)
4728 {
4729 break;
4730 }
4731 }
4732
4733 selected_larger_node |= new_range != old_range;
4734 Selection {
4735 id: selection.id,
4736 start: new_range.start,
4737 end: new_range.end,
4738 goal: SelectionGoal::None,
4739 reversed: selection.reversed,
4740 }
4741 })
4742 .collect::<Vec<_>>();
4743
4744 if selected_larger_node {
4745 stack.push(old_selections);
4746 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4747 s.select(new_selections);
4748 });
4749 }
4750 self.select_larger_syntax_node_stack = stack;
4751 }
4752
4753 pub fn select_smaller_syntax_node(
4754 &mut self,
4755 _: &SelectSmallerSyntaxNode,
4756 cx: &mut ViewContext<Self>,
4757 ) {
4758 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4759 if let Some(selections) = stack.pop() {
4760 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4761 s.select(selections.to_vec());
4762 });
4763 }
4764 self.select_larger_syntax_node_stack = stack;
4765 }
4766
4767 pub fn move_to_enclosing_bracket(
4768 &mut self,
4769 _: &MoveToEnclosingBracket,
4770 cx: &mut ViewContext<Self>,
4771 ) {
4772 let buffer = self.buffer.read(cx).snapshot(cx);
4773 let mut selections = self.selections.all::<usize>(cx);
4774 for selection in &mut selections {
4775 if let Some((open_range, close_range)) =
4776 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4777 {
4778 let close_range = close_range.to_inclusive();
4779 let destination = if close_range.contains(&selection.start)
4780 && close_range.contains(&selection.end)
4781 {
4782 open_range.end
4783 } else {
4784 *close_range.start()
4785 };
4786 selection.start = destination;
4787 selection.end = destination;
4788 }
4789 }
4790
4791 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4792 s.select(selections);
4793 });
4794 }
4795
4796 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4797 self.end_selection(cx);
4798 self.selection_history.mode = SelectionHistoryMode::Undoing;
4799 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4800 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4801 self.select_next_state = entry.select_next_state;
4802 self.add_selections_state = entry.add_selections_state;
4803 self.request_autoscroll(Autoscroll::Newest, cx);
4804 }
4805 self.selection_history.mode = SelectionHistoryMode::Normal;
4806 }
4807
4808 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4809 self.end_selection(cx);
4810 self.selection_history.mode = SelectionHistoryMode::Redoing;
4811 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4812 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4813 self.select_next_state = entry.select_next_state;
4814 self.add_selections_state = entry.add_selections_state;
4815 self.request_autoscroll(Autoscroll::Newest, cx);
4816 }
4817 self.selection_history.mode = SelectionHistoryMode::Normal;
4818 }
4819
4820 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
4821 self.go_to_diagnostic_impl(Direction::Next, cx)
4822 }
4823
4824 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4825 self.go_to_diagnostic_impl(Direction::Prev, cx)
4826 }
4827
4828 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4829 let buffer = self.buffer.read(cx).snapshot(cx);
4830 let selection = self.selections.newest::<usize>(cx);
4831
4832 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
4833 if direction == Direction::Next {
4834 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
4835 let (group_id, jump_to) = popover.activation_info();
4836 if self.activate_diagnostics(group_id, cx) {
4837 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4838 let mut new_selection = s.newest_anchor().clone();
4839 new_selection.collapse_to(jump_to, SelectionGoal::None);
4840 s.select_anchors(vec![new_selection.clone()]);
4841 });
4842 }
4843 return;
4844 }
4845 }
4846
4847 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4848 active_diagnostics
4849 .primary_range
4850 .to_offset(&buffer)
4851 .to_inclusive()
4852 });
4853 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4854 if active_primary_range.contains(&selection.head()) {
4855 *active_primary_range.end()
4856 } else {
4857 selection.head()
4858 }
4859 } else {
4860 selection.head()
4861 };
4862
4863 loop {
4864 let mut diagnostics = if direction == Direction::Prev {
4865 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4866 } else {
4867 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4868 };
4869 let group = diagnostics.find_map(|entry| {
4870 if entry.diagnostic.is_primary
4871 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4872 && !entry.range.is_empty()
4873 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4874 {
4875 Some((entry.range, entry.diagnostic.group_id))
4876 } else {
4877 None
4878 }
4879 });
4880
4881 if let Some((primary_range, group_id)) = group {
4882 if self.activate_diagnostics(group_id, cx) {
4883 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4884 s.select(vec![Selection {
4885 id: selection.id,
4886 start: primary_range.start,
4887 end: primary_range.start,
4888 reversed: false,
4889 goal: SelectionGoal::None,
4890 }]);
4891 });
4892 }
4893 break;
4894 } else {
4895 // Cycle around to the start of the buffer, potentially moving back to the start of
4896 // the currently active diagnostic.
4897 active_primary_range.take();
4898 if direction == Direction::Prev {
4899 if search_start == buffer.len() {
4900 break;
4901 } else {
4902 search_start = buffer.len();
4903 }
4904 } else if search_start == 0 {
4905 break;
4906 } else {
4907 search_start = 0;
4908 }
4909 }
4910 }
4911 }
4912
4913 pub fn go_to_definition(
4914 workspace: &mut Workspace,
4915 _: &GoToDefinition,
4916 cx: &mut ViewContext<Workspace>,
4917 ) {
4918 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
4919 }
4920
4921 pub fn go_to_type_definition(
4922 workspace: &mut Workspace,
4923 _: &GoToTypeDefinition,
4924 cx: &mut ViewContext<Workspace>,
4925 ) {
4926 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
4927 }
4928
4929 fn go_to_definition_of_kind(
4930 kind: GotoDefinitionKind,
4931 workspace: &mut Workspace,
4932 cx: &mut ViewContext<Workspace>,
4933 ) {
4934 let active_item = workspace.active_item(cx);
4935 let editor_handle = if let Some(editor) = active_item
4936 .as_ref()
4937 .and_then(|item| item.act_as::<Self>(cx))
4938 {
4939 editor
4940 } else {
4941 return;
4942 };
4943
4944 let editor = editor_handle.read(cx);
4945 let buffer = editor.buffer.read(cx);
4946 let head = editor.selections.newest::<usize>(cx).head();
4947 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4948 text_anchor
4949 } else {
4950 return;
4951 };
4952
4953 let project = workspace.project().clone();
4954 let definitions = project.update(cx, |project, cx| match kind {
4955 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
4956 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
4957 });
4958
4959 cx.spawn(|workspace, mut cx| async move {
4960 let definitions = definitions.await?;
4961 workspace.update(&mut cx, |workspace, cx| {
4962 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4963 });
4964
4965 Ok::<(), anyhow::Error>(())
4966 })
4967 .detach_and_log_err(cx);
4968 }
4969
4970 pub fn navigate_to_definitions(
4971 workspace: &mut Workspace,
4972 editor_handle: ViewHandle<Editor>,
4973 definitions: Vec<LocationLink>,
4974 cx: &mut ViewContext<Workspace>,
4975 ) {
4976 let pane = workspace.active_pane().clone();
4977 for definition in definitions {
4978 let range = definition
4979 .target
4980 .range
4981 .to_offset(definition.target.buffer.read(cx));
4982
4983 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4984 target_editor_handle.update(cx, |target_editor, cx| {
4985 // When selecting a definition in a different buffer, disable the nav history
4986 // to avoid creating a history entry at the previous cursor location.
4987 if editor_handle != target_editor_handle {
4988 pane.update(cx, |pane, _| pane.disable_history());
4989 }
4990 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4991 s.select_ranges([range]);
4992 });
4993
4994 pane.update(cx, |pane, _| pane.enable_history());
4995 });
4996 }
4997 }
4998
4999 pub fn find_all_references(
5000 workspace: &mut Workspace,
5001 _: &FindAllReferences,
5002 cx: &mut ViewContext<Workspace>,
5003 ) -> Option<Task<Result<()>>> {
5004 let active_item = workspace.active_item(cx)?;
5005 let editor_handle = active_item.act_as::<Self>(cx)?;
5006
5007 let editor = editor_handle.read(cx);
5008 let buffer = editor.buffer.read(cx);
5009 let head = editor.selections.newest::<usize>(cx).head();
5010 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5011 let replica_id = editor.replica_id(cx);
5012
5013 let project = workspace.project().clone();
5014 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5015 Some(cx.spawn(|workspace, mut cx| async move {
5016 let mut locations = references.await?;
5017 if locations.is_empty() {
5018 return Ok(());
5019 }
5020
5021 locations.sort_by_key(|location| location.buffer.id());
5022 let mut locations = locations.into_iter().peekable();
5023 let mut ranges_to_highlight = Vec::new();
5024
5025 let excerpt_buffer = cx.add_model(|cx| {
5026 let mut symbol_name = None;
5027 let mut multibuffer = MultiBuffer::new(replica_id);
5028 while let Some(location) = locations.next() {
5029 let buffer = location.buffer.read(cx);
5030 let mut ranges_for_buffer = Vec::new();
5031 let range = location.range.to_offset(buffer);
5032 ranges_for_buffer.push(range.clone());
5033 if symbol_name.is_none() {
5034 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
5035 }
5036
5037 while let Some(next_location) = locations.peek() {
5038 if next_location.buffer == location.buffer {
5039 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5040 locations.next();
5041 } else {
5042 break;
5043 }
5044 }
5045
5046 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5047 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5048 location.buffer.clone(),
5049 ranges_for_buffer,
5050 1,
5051 cx,
5052 ));
5053 }
5054 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
5055 });
5056
5057 workspace.update(&mut cx, |workspace, cx| {
5058 let editor =
5059 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
5060 editor.update(cx, |editor, cx| {
5061 editor.highlight_background::<Self>(
5062 ranges_to_highlight,
5063 |theme| theme.editor.highlighted_line_background,
5064 cx,
5065 );
5066 });
5067 workspace.add_item(Box::new(editor), cx);
5068 });
5069
5070 Ok(())
5071 }))
5072 }
5073
5074 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5075 use language::ToOffset as _;
5076
5077 let project = self.project.clone()?;
5078 let selection = self.selections.newest_anchor().clone();
5079 let (cursor_buffer, cursor_buffer_position) = self
5080 .buffer
5081 .read(cx)
5082 .text_anchor_for_position(selection.head(), cx)?;
5083 let (tail_buffer, _) = self
5084 .buffer
5085 .read(cx)
5086 .text_anchor_for_position(selection.tail(), cx)?;
5087 if tail_buffer != cursor_buffer {
5088 return None;
5089 }
5090
5091 let snapshot = cursor_buffer.read(cx).snapshot();
5092 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5093 let prepare_rename = project.update(cx, |project, cx| {
5094 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5095 });
5096
5097 Some(cx.spawn(|this, mut cx| async move {
5098 let rename_range = if let Some(range) = prepare_rename.await? {
5099 Some(range)
5100 } else {
5101 this.read_with(&cx, |this, cx| {
5102 let buffer = this.buffer.read(cx).snapshot(cx);
5103 let mut buffer_highlights = this
5104 .document_highlights_for_position(selection.head(), &buffer)
5105 .filter(|highlight| {
5106 highlight.start.excerpt_id() == selection.head().excerpt_id()
5107 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5108 });
5109 buffer_highlights
5110 .next()
5111 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5112 })
5113 };
5114 if let Some(rename_range) = rename_range {
5115 let rename_buffer_range = rename_range.to_offset(&snapshot);
5116 let cursor_offset_in_rename_range =
5117 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5118
5119 this.update(&mut cx, |this, cx| {
5120 this.take_rename(false, cx);
5121 let style = this.style(cx);
5122 let buffer = this.buffer.read(cx).read(cx);
5123 let cursor_offset = selection.head().to_offset(&buffer);
5124 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5125 let rename_end = rename_start + rename_buffer_range.len();
5126 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5127 let mut old_highlight_id = None;
5128 let old_name: Arc<str> = buffer
5129 .chunks(rename_start..rename_end, true)
5130 .map(|chunk| {
5131 if old_highlight_id.is_none() {
5132 old_highlight_id = chunk.syntax_highlight_id;
5133 }
5134 chunk.text
5135 })
5136 .collect::<String>()
5137 .into();
5138
5139 drop(buffer);
5140
5141 // Position the selection in the rename editor so that it matches the current selection.
5142 this.show_local_selections = false;
5143 let rename_editor = cx.add_view(|cx| {
5144 let mut editor = Editor::single_line(None, cx);
5145 if let Some(old_highlight_id) = old_highlight_id {
5146 editor.override_text_style =
5147 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5148 }
5149 editor.buffer.update(cx, |buffer, cx| {
5150 buffer.edit([(0..0, old_name.clone())], None, cx)
5151 });
5152 editor.select_all(&SelectAll, cx);
5153 editor
5154 });
5155
5156 let ranges = this
5157 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5158 .into_iter()
5159 .flat_map(|(_, ranges)| ranges)
5160 .chain(
5161 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5162 .into_iter()
5163 .flat_map(|(_, ranges)| ranges),
5164 )
5165 .collect();
5166
5167 this.highlight_text::<Rename>(
5168 ranges,
5169 HighlightStyle {
5170 fade_out: Some(style.rename_fade),
5171 ..Default::default()
5172 },
5173 cx,
5174 );
5175 cx.focus(&rename_editor);
5176 let block_id = this.insert_blocks(
5177 [BlockProperties {
5178 style: BlockStyle::Flex,
5179 position: range.start.clone(),
5180 height: 1,
5181 render: Arc::new({
5182 let editor = rename_editor.clone();
5183 move |cx: &mut BlockContext| {
5184 ChildView::new(editor.clone())
5185 .contained()
5186 .with_padding_left(cx.anchor_x)
5187 .boxed()
5188 }
5189 }),
5190 disposition: BlockDisposition::Below,
5191 }],
5192 cx,
5193 )[0];
5194 this.pending_rename = Some(RenameState {
5195 range,
5196 old_name,
5197 editor: rename_editor,
5198 block_id,
5199 });
5200 });
5201 }
5202
5203 Ok(())
5204 }))
5205 }
5206
5207 pub fn confirm_rename(
5208 workspace: &mut Workspace,
5209 _: &ConfirmRename,
5210 cx: &mut ViewContext<Workspace>,
5211 ) -> Option<Task<Result<()>>> {
5212 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5213
5214 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5215 let rename = editor.take_rename(false, cx)?;
5216 let buffer = editor.buffer.read(cx);
5217 let (start_buffer, start) =
5218 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5219 let (end_buffer, end) =
5220 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5221 if start_buffer == end_buffer {
5222 let new_name = rename.editor.read(cx).text(cx);
5223 Some((start_buffer, start..end, rename.old_name, new_name))
5224 } else {
5225 None
5226 }
5227 })?;
5228
5229 let rename = workspace.project().clone().update(cx, |project, cx| {
5230 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5231 });
5232
5233 Some(cx.spawn(|workspace, mut cx| async move {
5234 let project_transaction = rename.await?;
5235 Self::open_project_transaction(
5236 editor.clone(),
5237 workspace,
5238 project_transaction,
5239 format!("Rename: {} → {}", old_name, new_name),
5240 cx.clone(),
5241 )
5242 .await?;
5243
5244 editor.update(&mut cx, |editor, cx| {
5245 editor.refresh_document_highlights(cx);
5246 });
5247 Ok(())
5248 }))
5249 }
5250
5251 fn take_rename(
5252 &mut self,
5253 moving_cursor: bool,
5254 cx: &mut ViewContext<Self>,
5255 ) -> Option<RenameState> {
5256 let rename = self.pending_rename.take()?;
5257 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5258 self.clear_text_highlights::<Rename>(cx);
5259 self.show_local_selections = true;
5260
5261 if moving_cursor {
5262 let rename_editor = rename.editor.read(cx);
5263 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5264
5265 // Update the selection to match the position of the selection inside
5266 // the rename editor.
5267 let snapshot = self.buffer.read(cx).read(cx);
5268 let rename_range = rename.range.to_offset(&snapshot);
5269 let cursor_in_editor = snapshot
5270 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5271 .min(rename_range.end);
5272 drop(snapshot);
5273
5274 self.change_selections(None, cx, |s| {
5275 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5276 });
5277 } else {
5278 self.refresh_document_highlights(cx);
5279 }
5280
5281 Some(rename)
5282 }
5283
5284 #[cfg(any(test, feature = "test-support"))]
5285 pub fn pending_rename(&self) -> Option<&RenameState> {
5286 self.pending_rename.as_ref()
5287 }
5288
5289 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5290 let project = match &self.project {
5291 Some(project) => project.clone(),
5292 None => return None,
5293 };
5294
5295 Some(self.perform_format(project, cx))
5296 }
5297
5298 fn perform_format(
5299 &mut self,
5300 project: ModelHandle<Project>,
5301 cx: &mut ViewContext<'_, Self>,
5302 ) -> Task<Result<()>> {
5303 let buffer = self.buffer().clone();
5304 let buffers = buffer.read(cx).all_buffers();
5305
5306 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5307 let format = project.update(cx, |project, cx| {
5308 project.format(buffers, true, FormatTrigger::Manual, cx)
5309 });
5310
5311 cx.spawn(|_, mut cx| async move {
5312 let transaction = futures::select_biased! {
5313 _ = timeout => {
5314 log::warn!("timed out waiting for formatting");
5315 None
5316 }
5317 transaction = format.log_err().fuse() => transaction,
5318 };
5319
5320 buffer.update(&mut cx, |buffer, cx| {
5321 if let Some(transaction) = transaction {
5322 if !buffer.is_singleton() {
5323 buffer.push_transaction(&transaction.0);
5324 }
5325 }
5326
5327 cx.notify();
5328 });
5329
5330 Ok(())
5331 })
5332 }
5333
5334 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5335 if let Some(project) = self.project.clone() {
5336 self.buffer.update(cx, |multi_buffer, cx| {
5337 project.update(cx, |project, cx| {
5338 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5339 });
5340 })
5341 }
5342 }
5343
5344 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5345 cx.show_character_palette();
5346 }
5347
5348 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5349 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5350 let buffer = self.buffer.read(cx).snapshot(cx);
5351 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5352 let is_valid = buffer
5353 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5354 .any(|entry| {
5355 entry.diagnostic.is_primary
5356 && !entry.range.is_empty()
5357 && entry.range.start == primary_range_start
5358 && entry.diagnostic.message == active_diagnostics.primary_message
5359 });
5360
5361 if is_valid != active_diagnostics.is_valid {
5362 active_diagnostics.is_valid = is_valid;
5363 let mut new_styles = HashMap::default();
5364 for (block_id, diagnostic) in &active_diagnostics.blocks {
5365 new_styles.insert(
5366 *block_id,
5367 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5368 );
5369 }
5370 self.display_map
5371 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5372 }
5373 }
5374 }
5375
5376 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5377 self.dismiss_diagnostics(cx);
5378 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5379 let buffer = self.buffer.read(cx).snapshot(cx);
5380
5381 let mut primary_range = None;
5382 let mut primary_message = None;
5383 let mut group_end = Point::zero();
5384 let diagnostic_group = buffer
5385 .diagnostic_group::<Point>(group_id)
5386 .map(|entry| {
5387 if entry.range.end > group_end {
5388 group_end = entry.range.end;
5389 }
5390 if entry.diagnostic.is_primary {
5391 primary_range = Some(entry.range.clone());
5392 primary_message = Some(entry.diagnostic.message.clone());
5393 }
5394 entry
5395 })
5396 .collect::<Vec<_>>();
5397 let primary_range = primary_range?;
5398 let primary_message = primary_message?;
5399 let primary_range =
5400 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5401
5402 let blocks = display_map
5403 .insert_blocks(
5404 diagnostic_group.iter().map(|entry| {
5405 let diagnostic = entry.diagnostic.clone();
5406 let message_height = diagnostic.message.lines().count() as u8;
5407 BlockProperties {
5408 style: BlockStyle::Fixed,
5409 position: buffer.anchor_after(entry.range.start),
5410 height: message_height,
5411 render: diagnostic_block_renderer(diagnostic, true),
5412 disposition: BlockDisposition::Below,
5413 }
5414 }),
5415 cx,
5416 )
5417 .into_iter()
5418 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5419 .collect();
5420
5421 Some(ActiveDiagnosticGroup {
5422 primary_range,
5423 primary_message,
5424 blocks,
5425 is_valid: true,
5426 })
5427 });
5428 self.active_diagnostics.is_some()
5429 }
5430
5431 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5432 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5433 self.display_map.update(cx, |display_map, cx| {
5434 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5435 });
5436 cx.notify();
5437 }
5438 }
5439
5440 pub fn set_selections_from_remote(
5441 &mut self,
5442 selections: Vec<Selection<Anchor>>,
5443 cx: &mut ViewContext<Self>,
5444 ) {
5445 let old_cursor_position = self.selections.newest_anchor().head();
5446 self.selections.change_with(cx, |s| {
5447 s.select_anchors(selections);
5448 });
5449 self.selections_did_change(false, &old_cursor_position, cx);
5450 }
5451
5452 fn push_to_selection_history(&mut self) {
5453 self.selection_history.push(SelectionHistoryEntry {
5454 selections: self.selections.disjoint_anchors(),
5455 select_next_state: self.select_next_state.clone(),
5456 add_selections_state: self.add_selections_state.clone(),
5457 });
5458 }
5459
5460 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5461 self.autoscroll_request = Some((autoscroll, true));
5462 cx.notify();
5463 }
5464
5465 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5466 self.autoscroll_request = Some((autoscroll, false));
5467 cx.notify();
5468 }
5469
5470 pub fn transact(
5471 &mut self,
5472 cx: &mut ViewContext<Self>,
5473 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5474 ) -> Option<TransactionId> {
5475 self.start_transaction_at(Instant::now(), cx);
5476 update(self, cx);
5477 self.end_transaction_at(Instant::now(), cx)
5478 }
5479
5480 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5481 self.end_selection(cx);
5482 if let Some(tx_id) = self
5483 .buffer
5484 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5485 {
5486 self.selection_history
5487 .insert_transaction(tx_id, self.selections.disjoint_anchors());
5488 }
5489 }
5490
5491 fn end_transaction_at(
5492 &mut self,
5493 now: Instant,
5494 cx: &mut ViewContext<Self>,
5495 ) -> Option<TransactionId> {
5496 if let Some(tx_id) = self
5497 .buffer
5498 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5499 {
5500 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5501 *end_selections = Some(self.selections.disjoint_anchors());
5502 } else {
5503 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5504 }
5505
5506 cx.emit(Event::Edited);
5507 Some(tx_id)
5508 } else {
5509 None
5510 }
5511 }
5512
5513 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5514 log::info!("Editor::page_up");
5515 }
5516
5517 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5518 log::info!("Editor::page_down");
5519 }
5520
5521 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5522 let mut fold_ranges = Vec::new();
5523
5524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5525 let selections = self.selections.all::<Point>(cx);
5526 for selection in selections {
5527 let range = selection.display_range(&display_map).sorted();
5528 let buffer_start_row = range.start.to_point(&display_map).row;
5529
5530 for row in (0..=range.end.row()).rev() {
5531 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5532 let fold_range = self.foldable_range_for_line(&display_map, row);
5533 if fold_range.end.row >= buffer_start_row {
5534 fold_ranges.push(fold_range);
5535 if row <= range.start.row() {
5536 break;
5537 }
5538 }
5539 }
5540 }
5541 }
5542
5543 self.fold_ranges(fold_ranges, cx);
5544 }
5545
5546 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5547 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5548 let buffer = &display_map.buffer_snapshot;
5549 let selections = self.selections.all::<Point>(cx);
5550 let ranges = selections
5551 .iter()
5552 .map(|s| {
5553 let range = s.display_range(&display_map).sorted();
5554 let mut start = range.start.to_point(&display_map);
5555 let mut end = range.end.to_point(&display_map);
5556 start.column = 0;
5557 end.column = buffer.line_len(end.row);
5558 start..end
5559 })
5560 .collect::<Vec<_>>();
5561 self.unfold_ranges(ranges, true, cx);
5562 }
5563
5564 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5565 let max_point = display_map.max_point();
5566 if display_row >= max_point.row() {
5567 false
5568 } else {
5569 let (start_indent, is_blank) = display_map.line_indent(display_row);
5570 if is_blank {
5571 false
5572 } else {
5573 for display_row in display_row + 1..=max_point.row() {
5574 let (indent, is_blank) = display_map.line_indent(display_row);
5575 if !is_blank {
5576 return indent > start_indent;
5577 }
5578 }
5579 false
5580 }
5581 }
5582 }
5583
5584 fn foldable_range_for_line(
5585 &self,
5586 display_map: &DisplaySnapshot,
5587 start_row: u32,
5588 ) -> Range<Point> {
5589 let max_point = display_map.max_point();
5590
5591 let (start_indent, _) = display_map.line_indent(start_row);
5592 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5593 let mut end = None;
5594 for row in start_row + 1..=max_point.row() {
5595 let (indent, is_blank) = display_map.line_indent(row);
5596 if !is_blank && indent <= start_indent {
5597 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5598 break;
5599 }
5600 }
5601
5602 let end = end.unwrap_or(max_point);
5603 start.to_point(display_map)..end.to_point(display_map)
5604 }
5605
5606 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5607 let selections = self.selections.all::<Point>(cx);
5608 let ranges = selections.into_iter().map(|s| s.start..s.end);
5609 self.fold_ranges(ranges, cx);
5610 }
5611
5612 pub fn fold_ranges<T: ToOffset>(
5613 &mut self,
5614 ranges: impl IntoIterator<Item = Range<T>>,
5615 cx: &mut ViewContext<Self>,
5616 ) {
5617 let mut ranges = ranges.into_iter().peekable();
5618 if ranges.peek().is_some() {
5619 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5620 self.request_autoscroll(Autoscroll::Fit, cx);
5621 cx.notify();
5622 }
5623 }
5624
5625 pub fn unfold_ranges<T: ToOffset>(
5626 &mut self,
5627 ranges: impl IntoIterator<Item = Range<T>>,
5628 inclusive: bool,
5629 cx: &mut ViewContext<Self>,
5630 ) {
5631 let mut ranges = ranges.into_iter().peekable();
5632 if ranges.peek().is_some() {
5633 self.display_map
5634 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5635 self.request_autoscroll(Autoscroll::Fit, cx);
5636 cx.notify();
5637 }
5638 }
5639
5640 pub fn insert_blocks(
5641 &mut self,
5642 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5643 cx: &mut ViewContext<Self>,
5644 ) -> Vec<BlockId> {
5645 let blocks = self
5646 .display_map
5647 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5648 self.request_autoscroll(Autoscroll::Fit, cx);
5649 blocks
5650 }
5651
5652 pub fn replace_blocks(
5653 &mut self,
5654 blocks: HashMap<BlockId, RenderBlock>,
5655 cx: &mut ViewContext<Self>,
5656 ) {
5657 self.display_map
5658 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5659 self.request_autoscroll(Autoscroll::Fit, cx);
5660 }
5661
5662 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5663 self.display_map.update(cx, |display_map, cx| {
5664 display_map.remove_blocks(block_ids, cx)
5665 });
5666 }
5667
5668 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5669 self.display_map
5670 .update(cx, |map, cx| map.snapshot(cx))
5671 .longest_row()
5672 }
5673
5674 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5675 self.display_map
5676 .update(cx, |map, cx| map.snapshot(cx))
5677 .max_point()
5678 }
5679
5680 pub fn text(&self, cx: &AppContext) -> String {
5681 self.buffer.read(cx).read(cx).text()
5682 }
5683
5684 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5685 self.transact(cx, |this, cx| {
5686 this.buffer
5687 .read(cx)
5688 .as_singleton()
5689 .expect("you can only call set_text on editors for singleton buffers")
5690 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5691 });
5692 }
5693
5694 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5695 self.display_map
5696 .update(cx, |map, cx| map.snapshot(cx))
5697 .text()
5698 }
5699
5700 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5701 let language_name = self
5702 .buffer
5703 .read(cx)
5704 .as_singleton()
5705 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5706 .map(|l| l.name());
5707
5708 let settings = cx.global::<Settings>();
5709 let mode = self
5710 .soft_wrap_mode_override
5711 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5712 match mode {
5713 settings::SoftWrap::None => SoftWrap::None,
5714 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5715 settings::SoftWrap::PreferredLineLength => {
5716 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5717 }
5718 }
5719 }
5720
5721 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5722 self.soft_wrap_mode_override = Some(mode);
5723 cx.notify();
5724 }
5725
5726 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5727 self.display_map
5728 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5729 }
5730
5731 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5732 self.highlighted_rows = rows;
5733 }
5734
5735 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5736 self.highlighted_rows.clone()
5737 }
5738
5739 pub fn highlight_background<T: 'static>(
5740 &mut self,
5741 ranges: Vec<Range<Anchor>>,
5742 color_fetcher: fn(&Theme) -> Color,
5743 cx: &mut ViewContext<Self>,
5744 ) {
5745 self.background_highlights
5746 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5747 cx.notify();
5748 }
5749
5750 #[allow(clippy::type_complexity)]
5751 pub fn clear_background_highlights<T: 'static>(
5752 &mut self,
5753 cx: &mut ViewContext<Self>,
5754 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5755 cx.notify();
5756 self.background_highlights.remove(&TypeId::of::<T>())
5757 }
5758
5759 #[cfg(feature = "test-support")]
5760 pub fn all_background_highlights(
5761 &mut self,
5762 cx: &mut ViewContext<Self>,
5763 ) -> Vec<(Range<DisplayPoint>, Color)> {
5764 let snapshot = self.snapshot(cx);
5765 let buffer = &snapshot.buffer_snapshot;
5766 let start = buffer.anchor_before(0);
5767 let end = buffer.anchor_after(buffer.len());
5768 let theme = cx.global::<Settings>().theme.as_ref();
5769 self.background_highlights_in_range(start..end, &snapshot, theme)
5770 }
5771
5772 fn document_highlights_for_position<'a>(
5773 &'a self,
5774 position: Anchor,
5775 buffer: &'a MultiBufferSnapshot,
5776 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5777 let read_highlights = self
5778 .background_highlights
5779 .get(&TypeId::of::<DocumentHighlightRead>())
5780 .map(|h| &h.1);
5781 let write_highlights = self
5782 .background_highlights
5783 .get(&TypeId::of::<DocumentHighlightWrite>())
5784 .map(|h| &h.1);
5785 let left_position = position.bias_left(buffer);
5786 let right_position = position.bias_right(buffer);
5787 read_highlights
5788 .into_iter()
5789 .chain(write_highlights)
5790 .flat_map(move |ranges| {
5791 let start_ix = match ranges.binary_search_by(|probe| {
5792 let cmp = probe.end.cmp(&left_position, buffer);
5793 if cmp.is_ge() {
5794 Ordering::Greater
5795 } else {
5796 Ordering::Less
5797 }
5798 }) {
5799 Ok(i) | Err(i) => i,
5800 };
5801
5802 let right_position = right_position.clone();
5803 ranges[start_ix..]
5804 .iter()
5805 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
5806 })
5807 }
5808
5809 pub fn background_highlights_in_range(
5810 &self,
5811 search_range: Range<Anchor>,
5812 display_snapshot: &DisplaySnapshot,
5813 theme: &Theme,
5814 ) -> Vec<(Range<DisplayPoint>, Color)> {
5815 let mut results = Vec::new();
5816 let buffer = &display_snapshot.buffer_snapshot;
5817 for (color_fetcher, ranges) in self.background_highlights.values() {
5818 let color = color_fetcher(theme);
5819 let start_ix = match ranges.binary_search_by(|probe| {
5820 let cmp = probe.end.cmp(&search_range.start, buffer);
5821 if cmp.is_gt() {
5822 Ordering::Greater
5823 } else {
5824 Ordering::Less
5825 }
5826 }) {
5827 Ok(i) | Err(i) => i,
5828 };
5829 for range in &ranges[start_ix..] {
5830 if range.start.cmp(&search_range.end, buffer).is_ge() {
5831 break;
5832 }
5833 let start = range
5834 .start
5835 .to_point(buffer)
5836 .to_display_point(display_snapshot);
5837 let end = range
5838 .end
5839 .to_point(buffer)
5840 .to_display_point(display_snapshot);
5841 results.push((start..end, color))
5842 }
5843 }
5844 results
5845 }
5846
5847 pub fn highlight_text<T: 'static>(
5848 &mut self,
5849 ranges: Vec<Range<Anchor>>,
5850 style: HighlightStyle,
5851 cx: &mut ViewContext<Self>,
5852 ) {
5853 self.display_map.update(cx, |map, _| {
5854 map.highlight_text(TypeId::of::<T>(), ranges, style)
5855 });
5856 cx.notify();
5857 }
5858
5859 pub fn text_highlights<'a, T: 'static>(
5860 &'a self,
5861 cx: &'a AppContext,
5862 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
5863 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
5864 }
5865
5866 pub fn clear_text_highlights<T: 'static>(
5867 &mut self,
5868 cx: &mut ViewContext<Self>,
5869 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5870 cx.notify();
5871 self.display_map
5872 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5873 }
5874
5875 fn next_blink_epoch(&mut self) -> usize {
5876 self.blink_epoch += 1;
5877 self.blink_epoch
5878 }
5879
5880 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5881 if !self.focused {
5882 return;
5883 }
5884
5885 self.show_local_cursors = true;
5886 cx.notify();
5887
5888 let epoch = self.next_blink_epoch();
5889 cx.spawn(|this, mut cx| {
5890 let this = this.downgrade();
5891 async move {
5892 Timer::after(CURSOR_BLINK_INTERVAL).await;
5893 if let Some(this) = this.upgrade(&cx) {
5894 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5895 }
5896 }
5897 })
5898 .detach();
5899 }
5900
5901 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5902 if epoch == self.blink_epoch {
5903 self.blinking_paused = false;
5904 self.blink_cursors(epoch, cx);
5905 }
5906 }
5907
5908 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5909 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5910 self.show_local_cursors = !self.show_local_cursors;
5911 cx.notify();
5912
5913 let epoch = self.next_blink_epoch();
5914 cx.spawn(|this, mut cx| {
5915 let this = this.downgrade();
5916 async move {
5917 Timer::after(CURSOR_BLINK_INTERVAL).await;
5918 if let Some(this) = this.upgrade(&cx) {
5919 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5920 }
5921 }
5922 })
5923 .detach();
5924 }
5925 }
5926
5927 pub fn show_local_cursors(&self) -> bool {
5928 self.show_local_cursors && self.focused
5929 }
5930
5931 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5932 cx.notify();
5933 }
5934
5935 fn on_buffer_event(
5936 &mut self,
5937 _: ModelHandle<MultiBuffer>,
5938 event: &language::Event,
5939 cx: &mut ViewContext<Self>,
5940 ) {
5941 match event {
5942 language::Event::Edited => {
5943 self.refresh_active_diagnostics(cx);
5944 self.refresh_code_actions(cx);
5945 cx.emit(Event::BufferEdited);
5946 }
5947 language::Event::Reparsed => cx.emit(Event::Reparsed),
5948 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5949 language::Event::Saved => cx.emit(Event::Saved),
5950 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5951 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5952 language::Event::Closed => cx.emit(Event::Closed),
5953 language::Event::DiagnosticsUpdated => {
5954 self.refresh_active_diagnostics(cx);
5955 }
5956 _ => {}
5957 }
5958 }
5959
5960 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5961 cx.notify();
5962 }
5963
5964 pub fn set_searchable(&mut self, searchable: bool) {
5965 self.searchable = searchable;
5966 }
5967
5968 pub fn searchable(&self) -> bool {
5969 self.searchable
5970 }
5971
5972 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5973 let active_item = workspace.active_item(cx);
5974 let editor_handle = if let Some(editor) = active_item
5975 .as_ref()
5976 .and_then(|item| item.act_as::<Self>(cx))
5977 {
5978 editor
5979 } else {
5980 cx.propagate_action();
5981 return;
5982 };
5983
5984 let editor = editor_handle.read(cx);
5985 let buffer = editor.buffer.read(cx);
5986 if buffer.is_singleton() {
5987 cx.propagate_action();
5988 return;
5989 }
5990
5991 let mut new_selections_by_buffer = HashMap::default();
5992 for selection in editor.selections.all::<usize>(cx) {
5993 for (buffer, mut range) in
5994 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5995 {
5996 if selection.reversed {
5997 mem::swap(&mut range.start, &mut range.end);
5998 }
5999 new_selections_by_buffer
6000 .entry(buffer)
6001 .or_insert(Vec::new())
6002 .push(range)
6003 }
6004 }
6005
6006 editor_handle.update(cx, |editor, cx| {
6007 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6008 });
6009 let pane = workspace.active_pane().clone();
6010 pane.update(cx, |pane, _| pane.disable_history());
6011
6012 // We defer the pane interaction because we ourselves are a workspace item
6013 // and activating a new item causes the pane to call a method on us reentrantly,
6014 // which panics if we're on the stack.
6015 cx.defer(move |workspace, cx| {
6016 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6017 let editor = workspace.open_project_item::<Self>(buffer, cx);
6018 editor.update(cx, |editor, cx| {
6019 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
6020 s.select_ranges(ranges);
6021 });
6022 });
6023 }
6024
6025 pane.update(cx, |pane, _| pane.enable_history());
6026 });
6027 }
6028
6029 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6030 let editor = workspace.open_path(action.path.clone(), true, cx);
6031 let position = action.position;
6032 let anchor = action.anchor;
6033 cx.spawn_weak(|_, mut cx| async move {
6034 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6035 editor.update(&mut cx, |editor, cx| {
6036 let buffer = editor.buffer().read(cx).as_singleton()?;
6037 let buffer = buffer.read(cx);
6038 let cursor = if buffer.can_resolve(&anchor) {
6039 language::ToPoint::to_point(&anchor, buffer)
6040 } else {
6041 buffer.clip_point(position, Bias::Left)
6042 };
6043
6044 let nav_history = editor.nav_history.take();
6045 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
6046 s.select_ranges([cursor..cursor]);
6047 });
6048 editor.nav_history = nav_history;
6049
6050 Some(())
6051 })?;
6052 Some(())
6053 })
6054 .detach()
6055 }
6056
6057 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6058 let snapshot = self.buffer.read(cx).read(cx);
6059 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6060 Some(
6061 ranges
6062 .iter()
6063 .map(move |range| {
6064 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6065 })
6066 .collect(),
6067 )
6068 }
6069
6070 fn selection_replacement_ranges(
6071 &self,
6072 range: Range<OffsetUtf16>,
6073 cx: &AppContext,
6074 ) -> Vec<Range<OffsetUtf16>> {
6075 let selections = self.selections.all::<OffsetUtf16>(cx);
6076 let newest_selection = selections
6077 .iter()
6078 .max_by_key(|selection| selection.id)
6079 .unwrap();
6080 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6081 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6082 let snapshot = self.buffer.read(cx).read(cx);
6083 selections
6084 .into_iter()
6085 .map(|mut selection| {
6086 selection.start.0 =
6087 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6088 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6089 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6090 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6091 })
6092 .collect()
6093 }
6094
6095 fn report_event(&self, name: &str, cx: &AppContext) {
6096 if let Some((project, file)) = self.project.as_ref().zip(
6097 self.buffer
6098 .read(cx)
6099 .as_singleton()
6100 .and_then(|b| b.read(cx).file()),
6101 ) {
6102 project.read(cx).client().report_event(
6103 name,
6104 json!({
6105 "file_extension": file
6106 .path()
6107 .extension()
6108 .and_then(|e| e.to_str())
6109 }),
6110 );
6111 }
6112 }
6113}
6114
6115impl EditorSnapshot {
6116 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6117 self.display_snapshot.buffer_snapshot.language_at(position)
6118 }
6119
6120 pub fn is_focused(&self) -> bool {
6121 self.is_focused
6122 }
6123
6124 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6125 self.placeholder_text.as_ref()
6126 }
6127
6128 pub fn scroll_position(&self) -> Vector2F {
6129 compute_scroll_position(
6130 &self.display_snapshot,
6131 self.scroll_position,
6132 &self.scroll_top_anchor,
6133 )
6134 }
6135}
6136
6137impl Deref for EditorSnapshot {
6138 type Target = DisplaySnapshot;
6139
6140 fn deref(&self) -> &Self::Target {
6141 &self.display_snapshot
6142 }
6143}
6144
6145fn compute_scroll_position(
6146 snapshot: &DisplaySnapshot,
6147 mut scroll_position: Vector2F,
6148 scroll_top_anchor: &Anchor,
6149) -> Vector2F {
6150 if *scroll_top_anchor != Anchor::min() {
6151 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
6152 scroll_position.set_y(scroll_top + scroll_position.y());
6153 } else {
6154 scroll_position.set_y(0.);
6155 }
6156 scroll_position
6157}
6158
6159#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6160pub enum Event {
6161 BufferEdited,
6162 Edited,
6163 Reparsed,
6164 Blurred,
6165 DirtyChanged,
6166 Saved,
6167 TitleChanged,
6168 SelectionsChanged { local: bool },
6169 ScrollPositionChanged { local: bool },
6170 Closed,
6171 IgnoredInput,
6172}
6173
6174pub struct EditorFocused(pub ViewHandle<Editor>);
6175pub struct EditorBlurred(pub ViewHandle<Editor>);
6176pub struct EditorReleased(pub WeakViewHandle<Editor>);
6177
6178impl Entity for Editor {
6179 type Event = Event;
6180
6181 fn release(&mut self, cx: &mut MutableAppContext) {
6182 cx.emit_global(EditorReleased(self.handle.clone()));
6183 }
6184}
6185
6186impl View for Editor {
6187 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6188 let style = self.style(cx);
6189 let font_changed = self.display_map.update(cx, |map, cx| {
6190 map.set_font(style.text.font_id, style.text.font_size, cx)
6191 });
6192
6193 if font_changed {
6194 let handle = self.handle.clone();
6195 cx.defer(move |cx| {
6196 if let Some(editor) = handle.upgrade(cx) {
6197 editor.update(cx, |editor, cx| {
6198 hide_hover(editor, cx);
6199 hide_link_definition(editor, cx);
6200 })
6201 }
6202 });
6203 }
6204
6205 Stack::new()
6206 .with_child(
6207 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
6208 )
6209 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
6210 .boxed()
6211 }
6212
6213 fn ui_name() -> &'static str {
6214 "Editor"
6215 }
6216
6217 fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6218 let focused_event = EditorFocused(cx.handle());
6219 cx.emit_global(focused_event);
6220 if let Some(rename) = self.pending_rename.as_ref() {
6221 cx.focus(&rename.editor);
6222 } else {
6223 self.focused = true;
6224 self.blink_cursors(self.blink_epoch, cx);
6225 self.buffer.update(cx, |buffer, cx| {
6226 buffer.finalize_last_transaction(cx);
6227 if self.leader_replica_id.is_none() {
6228 buffer.set_active_selections(
6229 &self.selections.disjoint_anchors(),
6230 self.selections.line_mode,
6231 cx,
6232 );
6233 }
6234 });
6235 }
6236 }
6237
6238 fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6239 let blurred_event = EditorBlurred(cx.handle());
6240 cx.emit_global(blurred_event);
6241 self.focused = false;
6242 self.buffer
6243 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6244 self.hide_context_menu(cx);
6245 hide_hover(self, cx);
6246 cx.emit(Event::Blurred);
6247 cx.notify();
6248 }
6249
6250 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6251 let mut context = Self::default_keymap_context();
6252 let mode = match self.mode {
6253 EditorMode::SingleLine => "single_line",
6254 EditorMode::AutoHeight { .. } => "auto_height",
6255 EditorMode::Full => "full",
6256 };
6257 context.map.insert("mode".into(), mode.into());
6258 if self.pending_rename.is_some() {
6259 context.set.insert("renaming".into());
6260 }
6261 match self.context_menu.as_ref() {
6262 Some(ContextMenu::Completions(_)) => {
6263 context.set.insert("showing_completions".into());
6264 }
6265 Some(ContextMenu::CodeActions(_)) => {
6266 context.set.insert("showing_code_actions".into());
6267 }
6268 None => {}
6269 }
6270
6271 for layer in self.keymap_context_layers.values() {
6272 context.extend(layer);
6273 }
6274
6275 context
6276 }
6277
6278 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6279 Some(
6280 self.buffer
6281 .read(cx)
6282 .read(cx)
6283 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6284 .collect(),
6285 )
6286 }
6287
6288 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6289 // Prevent the IME menu from appearing when holding down an alphabetic key
6290 // while input is disabled.
6291 if !self.input_enabled {
6292 return None;
6293 }
6294
6295 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6296 Some(range.start.0..range.end.0)
6297 }
6298
6299 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6300 let snapshot = self.buffer.read(cx).read(cx);
6301 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6302 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6303 }
6304
6305 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6306 self.clear_text_highlights::<InputComposition>(cx);
6307 self.ime_transaction.take();
6308 }
6309
6310 fn replace_text_in_range(
6311 &mut self,
6312 range_utf16: Option<Range<usize>>,
6313 text: &str,
6314 cx: &mut ViewContext<Self>,
6315 ) {
6316 if !self.input_enabled {
6317 cx.emit(Event::IgnoredInput);
6318 return;
6319 }
6320
6321 self.transact(cx, |this, cx| {
6322 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6323 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6324 Some(this.selection_replacement_ranges(range_utf16, cx))
6325 } else {
6326 this.marked_text_ranges(cx)
6327 };
6328
6329 if let Some(new_selected_ranges) = new_selected_ranges {
6330 this.change_selections(None, cx, |selections| {
6331 selections.select_ranges(new_selected_ranges)
6332 });
6333 }
6334 this.handle_input(text, cx);
6335 });
6336
6337 if let Some(transaction) = self.ime_transaction {
6338 self.buffer.update(cx, |buffer, cx| {
6339 buffer.group_until_transaction(transaction, cx);
6340 });
6341 }
6342
6343 self.unmark_text(cx);
6344 }
6345
6346 fn replace_and_mark_text_in_range(
6347 &mut self,
6348 range_utf16: Option<Range<usize>>,
6349 text: &str,
6350 new_selected_range_utf16: Option<Range<usize>>,
6351 cx: &mut ViewContext<Self>,
6352 ) {
6353 if !self.input_enabled {
6354 cx.emit(Event::IgnoredInput);
6355 return;
6356 }
6357
6358 let transaction = self.transact(cx, |this, cx| {
6359 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6360 let snapshot = this.buffer.read(cx).read(cx);
6361 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6362 for marked_range in &mut marked_ranges {
6363 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6364 marked_range.start.0 += relative_range_utf16.start;
6365 marked_range.start =
6366 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6367 marked_range.end =
6368 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6369 }
6370 }
6371 Some(marked_ranges)
6372 } else if let Some(range_utf16) = range_utf16 {
6373 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6374 Some(this.selection_replacement_ranges(range_utf16, cx))
6375 } else {
6376 None
6377 };
6378
6379 if let Some(ranges) = ranges_to_replace {
6380 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6381 }
6382
6383 let marked_ranges = {
6384 let snapshot = this.buffer.read(cx).read(cx);
6385 this.selections
6386 .disjoint_anchors()
6387 .iter()
6388 .map(|selection| {
6389 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6390 })
6391 .collect::<Vec<_>>()
6392 };
6393
6394 if text.is_empty() {
6395 this.unmark_text(cx);
6396 } else {
6397 this.highlight_text::<InputComposition>(
6398 marked_ranges.clone(),
6399 this.style(cx).composition_mark,
6400 cx,
6401 );
6402 }
6403
6404 this.handle_input(text, cx);
6405
6406 if let Some(new_selected_range) = new_selected_range_utf16 {
6407 let snapshot = this.buffer.read(cx).read(cx);
6408 let new_selected_ranges = marked_ranges
6409 .into_iter()
6410 .map(|marked_range| {
6411 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6412 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6413 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6414 snapshot.clip_offset_utf16(new_start, Bias::Left)
6415 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6416 })
6417 .collect::<Vec<_>>();
6418
6419 drop(snapshot);
6420 this.change_selections(None, cx, |selections| {
6421 selections.select_ranges(new_selected_ranges)
6422 });
6423 }
6424 });
6425
6426 self.ime_transaction = self.ime_transaction.or(transaction);
6427 if let Some(transaction) = self.ime_transaction {
6428 self.buffer.update(cx, |buffer, cx| {
6429 buffer.group_until_transaction(transaction, cx);
6430 });
6431 }
6432
6433 if self.text_highlights::<InputComposition>(cx).is_none() {
6434 self.ime_transaction.take();
6435 }
6436 }
6437}
6438
6439fn build_style(
6440 settings: &Settings,
6441 get_field_editor_theme: Option<GetFieldEditorTheme>,
6442 override_text_style: Option<&OverrideTextStyle>,
6443 cx: &AppContext,
6444) -> EditorStyle {
6445 let font_cache = cx.font_cache();
6446
6447 let mut theme = settings.theme.editor.clone();
6448 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6449 let field_editor_theme = get_field_editor_theme(&settings.theme);
6450 theme.text_color = field_editor_theme.text.color;
6451 theme.selection = field_editor_theme.selection;
6452 theme.background = field_editor_theme
6453 .container
6454 .background_color
6455 .unwrap_or_default();
6456 EditorStyle {
6457 text: field_editor_theme.text,
6458 placeholder_text: field_editor_theme.placeholder_text,
6459 theme,
6460 }
6461 } else {
6462 let font_family_id = settings.buffer_font_family;
6463 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6464 let font_properties = Default::default();
6465 let font_id = font_cache
6466 .select_font(font_family_id, &font_properties)
6467 .unwrap();
6468 let font_size = settings.buffer_font_size;
6469 EditorStyle {
6470 text: TextStyle {
6471 color: settings.theme.editor.text_color,
6472 font_family_name,
6473 font_family_id,
6474 font_id,
6475 font_size,
6476 font_properties,
6477 underline: Default::default(),
6478 },
6479 placeholder_text: None,
6480 theme,
6481 }
6482 };
6483
6484 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6485 if let Some(highlighted) = style
6486 .text
6487 .clone()
6488 .highlight(highlight_style, font_cache)
6489 .log_err()
6490 {
6491 style.text = highlighted;
6492 }
6493 }
6494
6495 style
6496}
6497
6498trait SelectionExt {
6499 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6500 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6501 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6502 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6503 -> Range<u32>;
6504}
6505
6506impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6507 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6508 let start = self.start.to_point(buffer);
6509 let end = self.end.to_point(buffer);
6510 if self.reversed {
6511 end..start
6512 } else {
6513 start..end
6514 }
6515 }
6516
6517 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6518 let start = self.start.to_offset(buffer);
6519 let end = self.end.to_offset(buffer);
6520 if self.reversed {
6521 end..start
6522 } else {
6523 start..end
6524 }
6525 }
6526
6527 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6528 let start = self
6529 .start
6530 .to_point(&map.buffer_snapshot)
6531 .to_display_point(map);
6532 let end = self
6533 .end
6534 .to_point(&map.buffer_snapshot)
6535 .to_display_point(map);
6536 if self.reversed {
6537 end..start
6538 } else {
6539 start..end
6540 }
6541 }
6542
6543 fn spanned_rows(
6544 &self,
6545 include_end_if_at_line_start: bool,
6546 map: &DisplaySnapshot,
6547 ) -> Range<u32> {
6548 let start = self.start.to_point(&map.buffer_snapshot);
6549 let mut end = self.end.to_point(&map.buffer_snapshot);
6550 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6551 end.row -= 1;
6552 }
6553
6554 let buffer_start = map.prev_line_boundary(start).0;
6555 let buffer_end = map.next_line_boundary(end).0;
6556 buffer_start.row..buffer_end.row + 1
6557 }
6558}
6559
6560impl<T: InvalidationRegion> InvalidationStack<T> {
6561 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6562 where
6563 S: Clone + ToOffset,
6564 {
6565 while let Some(region) = self.last() {
6566 let all_selections_inside_invalidation_ranges =
6567 if selections.len() == region.ranges().len() {
6568 selections
6569 .iter()
6570 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
6571 .all(|(selection, invalidation_range)| {
6572 let head = selection.head().to_offset(buffer);
6573 invalidation_range.start <= head && invalidation_range.end >= head
6574 })
6575 } else {
6576 false
6577 };
6578
6579 if all_selections_inside_invalidation_ranges {
6580 break;
6581 } else {
6582 self.pop();
6583 }
6584 }
6585 }
6586}
6587
6588impl<T> Default for InvalidationStack<T> {
6589 fn default() -> Self {
6590 Self(Default::default())
6591 }
6592}
6593
6594impl<T> Deref for InvalidationStack<T> {
6595 type Target = Vec<T>;
6596
6597 fn deref(&self) -> &Self::Target {
6598 &self.0
6599 }
6600}
6601
6602impl<T> DerefMut for InvalidationStack<T> {
6603 fn deref_mut(&mut self) -> &mut Self::Target {
6604 &mut self.0
6605 }
6606}
6607
6608impl InvalidationRegion for SnippetState {
6609 fn ranges(&self) -> &[Range<Anchor>] {
6610 &self.ranges[self.active_index]
6611 }
6612}
6613
6614impl Deref for EditorStyle {
6615 type Target = theme::Editor;
6616
6617 fn deref(&self) -> &Self::Target {
6618 &self.theme
6619 }
6620}
6621
6622pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6623 let mut highlighted_lines = Vec::new();
6624 for line in diagnostic.message.lines() {
6625 highlighted_lines.push(highlight_diagnostic_message(line));
6626 }
6627
6628 Arc::new(move |cx: &mut BlockContext| {
6629 let settings = cx.global::<Settings>();
6630 let theme = &settings.theme.editor;
6631 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6632 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6633 Flex::column()
6634 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6635 Label::new(
6636 line.clone(),
6637 style.message.clone().with_font_size(font_size),
6638 )
6639 .with_highlights(highlights.clone())
6640 .contained()
6641 .with_margin_left(cx.anchor_x)
6642 .boxed()
6643 }))
6644 .aligned()
6645 .left()
6646 .boxed()
6647 })
6648}
6649
6650pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6651 let mut message_without_backticks = String::new();
6652 let mut prev_offset = 0;
6653 let mut inside_block = false;
6654 let mut highlights = Vec::new();
6655 for (match_ix, (offset, _)) in message
6656 .match_indices('`')
6657 .chain([(message.len(), "")])
6658 .enumerate()
6659 {
6660 message_without_backticks.push_str(&message[prev_offset..offset]);
6661 if inside_block {
6662 highlights.extend(prev_offset - match_ix..offset - match_ix);
6663 }
6664
6665 inside_block = !inside_block;
6666 prev_offset = offset + 1;
6667 }
6668
6669 (message_without_backticks, highlights)
6670}
6671
6672pub fn diagnostic_style(
6673 severity: DiagnosticSeverity,
6674 valid: bool,
6675 theme: &theme::Editor,
6676) -> DiagnosticStyle {
6677 match (severity, valid) {
6678 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6679 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6680 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6681 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6682 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6683 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6684 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6685 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6686 _ => theme.invalid_hint_diagnostic.clone(),
6687 }
6688}
6689
6690pub fn combine_syntax_and_fuzzy_match_highlights(
6691 text: &str,
6692 default_style: HighlightStyle,
6693 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6694 match_indices: &[usize],
6695) -> Vec<(Range<usize>, HighlightStyle)> {
6696 let mut result = Vec::new();
6697 let mut match_indices = match_indices.iter().copied().peekable();
6698
6699 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6700 {
6701 syntax_highlight.weight = None;
6702
6703 // Add highlights for any fuzzy match characters before the next
6704 // syntax highlight range.
6705 while let Some(&match_index) = match_indices.peek() {
6706 if match_index >= range.start {
6707 break;
6708 }
6709 match_indices.next();
6710 let end_index = char_ix_after(match_index, text);
6711 let mut match_style = default_style;
6712 match_style.weight = Some(fonts::Weight::BOLD);
6713 result.push((match_index..end_index, match_style));
6714 }
6715
6716 if range.start == usize::MAX {
6717 break;
6718 }
6719
6720 // Add highlights for any fuzzy match characters within the
6721 // syntax highlight range.
6722 let mut offset = range.start;
6723 while let Some(&match_index) = match_indices.peek() {
6724 if match_index >= range.end {
6725 break;
6726 }
6727
6728 match_indices.next();
6729 if match_index > offset {
6730 result.push((offset..match_index, syntax_highlight));
6731 }
6732
6733 let mut end_index = char_ix_after(match_index, text);
6734 while let Some(&next_match_index) = match_indices.peek() {
6735 if next_match_index == end_index && next_match_index < range.end {
6736 end_index = char_ix_after(next_match_index, text);
6737 match_indices.next();
6738 } else {
6739 break;
6740 }
6741 }
6742
6743 let mut match_style = syntax_highlight;
6744 match_style.weight = Some(fonts::Weight::BOLD);
6745 result.push((match_index..end_index, match_style));
6746 offset = end_index;
6747 }
6748
6749 if offset < range.end {
6750 result.push((offset..range.end, syntax_highlight));
6751 }
6752 }
6753
6754 fn char_ix_after(ix: usize, text: &str) -> usize {
6755 ix + text[ix..].chars().next().unwrap().len_utf8()
6756 }
6757
6758 result
6759}
6760
6761pub fn styled_runs_for_code_label<'a>(
6762 label: &'a CodeLabel,
6763 syntax_theme: &'a theme::SyntaxTheme,
6764) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6765 let fade_out = HighlightStyle {
6766 fade_out: Some(0.35),
6767 ..Default::default()
6768 };
6769
6770 let mut prev_end = label.filter_range.end;
6771 label
6772 .runs
6773 .iter()
6774 .enumerate()
6775 .flat_map(move |(ix, (range, highlight_id))| {
6776 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6777 style
6778 } else {
6779 return Default::default();
6780 };
6781 let mut muted_style = style;
6782 muted_style.highlight(fade_out);
6783
6784 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6785 if range.start >= label.filter_range.end {
6786 if range.start > prev_end {
6787 runs.push((prev_end..range.start, fade_out));
6788 }
6789 runs.push((range.clone(), muted_style));
6790 } else if range.end <= label.filter_range.end {
6791 runs.push((range.clone(), style));
6792 } else {
6793 runs.push((range.start..label.filter_range.end, style));
6794 runs.push((label.filter_range.end..range.end, muted_style));
6795 }
6796 prev_end = cmp::max(prev_end, range.end);
6797
6798 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6799 runs.push((prev_end..label.text.len(), fade_out));
6800 }
6801
6802 runs
6803 })
6804}
6805
6806#[cfg(test)]
6807mod tests {
6808 use crate::test::{
6809 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6810 EditorTestContext,
6811 };
6812
6813 use super::*;
6814 use futures::StreamExt;
6815 use gpui::{
6816 geometry::rect::RectF,
6817 platform::{WindowBounds, WindowOptions},
6818 };
6819 use indoc::indoc;
6820 use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry};
6821 use project::FakeFs;
6822 use settings::EditorSettings;
6823 use std::{cell::RefCell, rc::Rc, time::Instant};
6824 use text::Point;
6825 use unindent::Unindent;
6826 use util::{
6827 assert_set_eq,
6828 test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
6829 };
6830 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6831
6832 #[gpui::test]
6833 fn test_edit_events(cx: &mut MutableAppContext) {
6834 cx.set_global(Settings::test(cx));
6835 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6836
6837 let events = Rc::new(RefCell::new(Vec::new()));
6838 let (_, editor1) = cx.add_window(Default::default(), {
6839 let events = events.clone();
6840 |cx| {
6841 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6842 if matches!(
6843 event,
6844 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6845 ) {
6846 events.borrow_mut().push(("editor1", *event));
6847 }
6848 })
6849 .detach();
6850 Editor::for_buffer(buffer.clone(), None, cx)
6851 }
6852 });
6853 let (_, editor2) = cx.add_window(Default::default(), {
6854 let events = events.clone();
6855 |cx| {
6856 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6857 if matches!(
6858 event,
6859 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6860 ) {
6861 events.borrow_mut().push(("editor2", *event));
6862 }
6863 })
6864 .detach();
6865 Editor::for_buffer(buffer.clone(), None, cx)
6866 }
6867 });
6868 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6869
6870 // Mutating editor 1 will emit an `Edited` event only for that editor.
6871 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6872 assert_eq!(
6873 mem::take(&mut *events.borrow_mut()),
6874 [
6875 ("editor1", Event::Edited),
6876 ("editor1", Event::BufferEdited),
6877 ("editor2", Event::BufferEdited),
6878 ("editor1", Event::DirtyChanged),
6879 ("editor2", Event::DirtyChanged)
6880 ]
6881 );
6882
6883 // Mutating editor 2 will emit an `Edited` event only for that editor.
6884 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6885 assert_eq!(
6886 mem::take(&mut *events.borrow_mut()),
6887 [
6888 ("editor2", Event::Edited),
6889 ("editor1", Event::BufferEdited),
6890 ("editor2", Event::BufferEdited),
6891 ]
6892 );
6893
6894 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6895 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6896 assert_eq!(
6897 mem::take(&mut *events.borrow_mut()),
6898 [
6899 ("editor1", Event::Edited),
6900 ("editor1", Event::BufferEdited),
6901 ("editor2", Event::BufferEdited),
6902 ("editor1", Event::DirtyChanged),
6903 ("editor2", Event::DirtyChanged),
6904 ]
6905 );
6906
6907 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6908 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6909 assert_eq!(
6910 mem::take(&mut *events.borrow_mut()),
6911 [
6912 ("editor1", Event::Edited),
6913 ("editor1", Event::BufferEdited),
6914 ("editor2", Event::BufferEdited),
6915 ("editor1", Event::DirtyChanged),
6916 ("editor2", Event::DirtyChanged),
6917 ]
6918 );
6919
6920 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6921 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6922 assert_eq!(
6923 mem::take(&mut *events.borrow_mut()),
6924 [
6925 ("editor2", Event::Edited),
6926 ("editor1", Event::BufferEdited),
6927 ("editor2", Event::BufferEdited),
6928 ("editor1", Event::DirtyChanged),
6929 ("editor2", Event::DirtyChanged),
6930 ]
6931 );
6932
6933 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6934 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6935 assert_eq!(
6936 mem::take(&mut *events.borrow_mut()),
6937 [
6938 ("editor2", Event::Edited),
6939 ("editor1", Event::BufferEdited),
6940 ("editor2", Event::BufferEdited),
6941 ("editor1", Event::DirtyChanged),
6942 ("editor2", Event::DirtyChanged),
6943 ]
6944 );
6945
6946 // No event is emitted when the mutation is a no-op.
6947 editor2.update(cx, |editor, cx| {
6948 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6949
6950 editor.backspace(&Backspace, cx);
6951 });
6952 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6953 }
6954
6955 #[gpui::test]
6956 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6957 cx.set_global(Settings::test(cx));
6958 let mut now = Instant::now();
6959 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6960 let group_interval = buffer.read(cx).transaction_group_interval();
6961 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6962 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6963
6964 editor.update(cx, |editor, cx| {
6965 editor.start_transaction_at(now, cx);
6966 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6967
6968 editor.insert("cd", cx);
6969 editor.end_transaction_at(now, cx);
6970 assert_eq!(editor.text(cx), "12cd56");
6971 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6972
6973 editor.start_transaction_at(now, cx);
6974 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6975 editor.insert("e", cx);
6976 editor.end_transaction_at(now, cx);
6977 assert_eq!(editor.text(cx), "12cde6");
6978 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6979
6980 now += group_interval + Duration::from_millis(1);
6981 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6982
6983 // Simulate an edit in another editor
6984 buffer.update(cx, |buffer, cx| {
6985 buffer.start_transaction_at(now, cx);
6986 buffer.edit([(0..1, "a")], None, cx);
6987 buffer.edit([(1..1, "b")], None, cx);
6988 buffer.end_transaction_at(now, cx);
6989 });
6990
6991 assert_eq!(editor.text(cx), "ab2cde6");
6992 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6993
6994 // Last transaction happened past the group interval in a different editor.
6995 // Undo it individually and don't restore selections.
6996 editor.undo(&Undo, cx);
6997 assert_eq!(editor.text(cx), "12cde6");
6998 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6999
7000 // First two transactions happened within the group interval in this editor.
7001 // Undo them together and restore selections.
7002 editor.undo(&Undo, cx);
7003 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
7004 assert_eq!(editor.text(cx), "123456");
7005 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
7006
7007 // Redo the first two transactions together.
7008 editor.redo(&Redo, cx);
7009 assert_eq!(editor.text(cx), "12cde6");
7010 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
7011
7012 // Redo the last transaction on its own.
7013 editor.redo(&Redo, cx);
7014 assert_eq!(editor.text(cx), "ab2cde6");
7015 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
7016
7017 // Test empty transactions.
7018 editor.start_transaction_at(now, cx);
7019 editor.end_transaction_at(now, cx);
7020 editor.undo(&Undo, cx);
7021 assert_eq!(editor.text(cx), "12cde6");
7022 });
7023 }
7024
7025 #[gpui::test]
7026 fn test_ime_composition(cx: &mut MutableAppContext) {
7027 cx.set_global(Settings::test(cx));
7028 let buffer = cx.add_model(|cx| {
7029 let mut buffer = language::Buffer::new(0, "abcde", cx);
7030 // Ensure automatic grouping doesn't occur.
7031 buffer.set_group_interval(Duration::ZERO);
7032 buffer
7033 });
7034
7035 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7036 cx.add_window(Default::default(), |cx| {
7037 let mut editor = build_editor(buffer.clone(), cx);
7038
7039 // Start a new IME composition.
7040 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
7041 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
7042 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
7043 assert_eq!(editor.text(cx), "äbcde");
7044 assert_eq!(
7045 editor.marked_text_ranges(cx),
7046 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
7047 );
7048
7049 // Finalize IME composition.
7050 editor.replace_text_in_range(None, "ā", cx);
7051 assert_eq!(editor.text(cx), "ābcde");
7052 assert_eq!(editor.marked_text_ranges(cx), None);
7053
7054 // IME composition edits are grouped and are undone/redone at once.
7055 editor.undo(&Default::default(), cx);
7056 assert_eq!(editor.text(cx), "abcde");
7057 assert_eq!(editor.marked_text_ranges(cx), None);
7058 editor.redo(&Default::default(), cx);
7059 assert_eq!(editor.text(cx), "ābcde");
7060 assert_eq!(editor.marked_text_ranges(cx), None);
7061
7062 // Start a new IME composition.
7063 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
7064 assert_eq!(
7065 editor.marked_text_ranges(cx),
7066 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
7067 );
7068
7069 // Undoing during an IME composition cancels it.
7070 editor.undo(&Default::default(), cx);
7071 assert_eq!(editor.text(cx), "ābcde");
7072 assert_eq!(editor.marked_text_ranges(cx), None);
7073
7074 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
7075 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
7076 assert_eq!(editor.text(cx), "ābcdè");
7077 assert_eq!(
7078 editor.marked_text_ranges(cx),
7079 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
7080 );
7081
7082 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
7083 editor.replace_text_in_range(Some(4..999), "ę", cx);
7084 assert_eq!(editor.text(cx), "ābcdę");
7085 assert_eq!(editor.marked_text_ranges(cx), None);
7086
7087 // Start a new IME composition with multiple cursors.
7088 editor.change_selections(None, cx, |s| {
7089 s.select_ranges([
7090 OffsetUtf16(1)..OffsetUtf16(1),
7091 OffsetUtf16(3)..OffsetUtf16(3),
7092 OffsetUtf16(5)..OffsetUtf16(5),
7093 ])
7094 });
7095 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
7096 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
7097 assert_eq!(
7098 editor.marked_text_ranges(cx),
7099 Some(vec![
7100 OffsetUtf16(0)..OffsetUtf16(3),
7101 OffsetUtf16(4)..OffsetUtf16(7),
7102 OffsetUtf16(8)..OffsetUtf16(11)
7103 ])
7104 );
7105
7106 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
7107 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
7108 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
7109 assert_eq!(
7110 editor.marked_text_ranges(cx),
7111 Some(vec![
7112 OffsetUtf16(1)..OffsetUtf16(2),
7113 OffsetUtf16(5)..OffsetUtf16(6),
7114 OffsetUtf16(9)..OffsetUtf16(10)
7115 ])
7116 );
7117
7118 // Finalize IME composition with multiple cursors.
7119 editor.replace_text_in_range(Some(9..10), "2", cx);
7120 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
7121 assert_eq!(editor.marked_text_ranges(cx), None);
7122
7123 editor
7124 });
7125 }
7126
7127 #[gpui::test]
7128 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
7129 cx.set_global(Settings::test(cx));
7130
7131 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
7132 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7133 editor.update(cx, |view, cx| {
7134 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7135 });
7136 assert_eq!(
7137 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7138 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7139 );
7140
7141 editor.update(cx, |view, cx| {
7142 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7143 });
7144
7145 assert_eq!(
7146 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7147 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7148 );
7149
7150 editor.update(cx, |view, cx| {
7151 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7152 });
7153
7154 assert_eq!(
7155 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7156 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7157 );
7158
7159 editor.update(cx, |view, cx| {
7160 view.end_selection(cx);
7161 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7162 });
7163
7164 assert_eq!(
7165 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7166 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7167 );
7168
7169 editor.update(cx, |view, cx| {
7170 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
7171 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
7172 });
7173
7174 assert_eq!(
7175 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7176 [
7177 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
7178 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
7179 ]
7180 );
7181
7182 editor.update(cx, |view, cx| {
7183 view.end_selection(cx);
7184 });
7185
7186 assert_eq!(
7187 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7188 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
7189 );
7190 }
7191
7192 #[gpui::test]
7193 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
7194 cx.set_global(Settings::test(cx));
7195 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7196 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7197
7198 view.update(cx, |view, cx| {
7199 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7200 assert_eq!(
7201 view.selections.display_ranges(cx),
7202 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7203 );
7204 });
7205
7206 view.update(cx, |view, cx| {
7207 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7208 assert_eq!(
7209 view.selections.display_ranges(cx),
7210 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7211 );
7212 });
7213
7214 view.update(cx, |view, cx| {
7215 view.cancel(&Cancel, cx);
7216 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7217 assert_eq!(
7218 view.selections.display_ranges(cx),
7219 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7220 );
7221 });
7222 }
7223
7224 #[gpui::test]
7225 fn test_clone(cx: &mut gpui::MutableAppContext) {
7226 let (text, selection_ranges) = marked_text_ranges(
7227 indoc! {"
7228 one
7229 two
7230 threeˇ
7231 four
7232 fiveˇ
7233 "},
7234 true,
7235 );
7236 cx.set_global(Settings::test(cx));
7237 let buffer = MultiBuffer::build_simple(&text, cx);
7238
7239 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7240
7241 editor.update(cx, |editor, cx| {
7242 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
7243 editor.fold_ranges(
7244 [
7245 Point::new(1, 0)..Point::new(2, 0),
7246 Point::new(3, 0)..Point::new(4, 0),
7247 ],
7248 cx,
7249 );
7250 });
7251
7252 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
7253 cx.add_window(Default::default(), |cx| editor.clone(cx))
7254 });
7255
7256 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
7257 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
7258
7259 assert_eq!(
7260 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
7261 editor.update(cx, |e, cx| e.display_text(cx))
7262 );
7263 assert_eq!(
7264 cloned_snapshot
7265 .folds_in_range(0..text.len())
7266 .collect::<Vec<_>>(),
7267 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
7268 );
7269 assert_set_eq!(
7270 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7271 editor.read(cx).selections.ranges(cx)
7272 );
7273 assert_set_eq!(
7274 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7275 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7276 );
7277 }
7278
7279 #[gpui::test]
7280 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7281 cx.set_global(Settings::test(cx));
7282 use workspace::Item;
7283 let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx));
7284 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7285
7286 cx.add_view(&pane, |cx| {
7287 let mut editor = build_editor(buffer.clone(), cx);
7288 let handle = cx.handle();
7289 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7290
7291 fn pop_history(
7292 editor: &mut Editor,
7293 cx: &mut MutableAppContext,
7294 ) -> Option<NavigationEntry> {
7295 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7296 }
7297
7298 // Move the cursor a small distance.
7299 // Nothing is added to the navigation history.
7300 editor.change_selections(None, cx, |s| {
7301 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7302 });
7303 editor.change_selections(None, cx, |s| {
7304 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7305 });
7306 assert!(pop_history(&mut editor, cx).is_none());
7307
7308 // Move the cursor a large distance.
7309 // The history can jump back to the previous position.
7310 editor.change_selections(None, cx, |s| {
7311 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7312 });
7313 let nav_entry = pop_history(&mut editor, cx).unwrap();
7314 editor.navigate(nav_entry.data.unwrap(), cx);
7315 assert_eq!(nav_entry.item.id(), cx.view_id());
7316 assert_eq!(
7317 editor.selections.display_ranges(cx),
7318 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7319 );
7320 assert!(pop_history(&mut editor, cx).is_none());
7321
7322 // Move the cursor a small distance via the mouse.
7323 // Nothing is added to the navigation history.
7324 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7325 editor.end_selection(cx);
7326 assert_eq!(
7327 editor.selections.display_ranges(cx),
7328 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7329 );
7330 assert!(pop_history(&mut editor, cx).is_none());
7331
7332 // Move the cursor a large distance via the mouse.
7333 // The history can jump back to the previous position.
7334 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7335 editor.end_selection(cx);
7336 assert_eq!(
7337 editor.selections.display_ranges(cx),
7338 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7339 );
7340 let nav_entry = pop_history(&mut editor, cx).unwrap();
7341 editor.navigate(nav_entry.data.unwrap(), cx);
7342 assert_eq!(nav_entry.item.id(), cx.view_id());
7343 assert_eq!(
7344 editor.selections.display_ranges(cx),
7345 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7346 );
7347 assert!(pop_history(&mut editor, cx).is_none());
7348
7349 // Set scroll position to check later
7350 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7351 let original_scroll_position = editor.scroll_position;
7352 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7353
7354 // Jump to the end of the document and adjust scroll
7355 editor.move_to_end(&MoveToEnd, cx);
7356 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7357 assert_ne!(editor.scroll_position, original_scroll_position);
7358 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7359
7360 let nav_entry = pop_history(&mut editor, cx).unwrap();
7361 editor.navigate(nav_entry.data.unwrap(), cx);
7362 assert_eq!(editor.scroll_position, original_scroll_position);
7363 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7364
7365 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7366 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7367 invalid_anchor.text_anchor.buffer_id = Some(999);
7368 let invalid_point = Point::new(9999, 0);
7369 editor.navigate(
7370 Box::new(NavigationData {
7371 cursor_anchor: invalid_anchor.clone(),
7372 cursor_position: invalid_point,
7373 scroll_top_anchor: invalid_anchor,
7374 scroll_top_row: invalid_point.row,
7375 scroll_position: Default::default(),
7376 }),
7377 cx,
7378 );
7379 assert_eq!(
7380 editor.selections.display_ranges(cx),
7381 &[editor.max_point(cx)..editor.max_point(cx)]
7382 );
7383 assert_eq!(
7384 editor.scroll_position(cx),
7385 vec2f(0., editor.max_point(cx).row() as f32)
7386 );
7387
7388 editor
7389 });
7390 }
7391
7392 #[gpui::test]
7393 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7394 cx.set_global(Settings::test(cx));
7395 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7396 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7397
7398 view.update(cx, |view, cx| {
7399 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7400 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7401 view.end_selection(cx);
7402
7403 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7404 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7405 view.end_selection(cx);
7406 assert_eq!(
7407 view.selections.display_ranges(cx),
7408 [
7409 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7410 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7411 ]
7412 );
7413 });
7414
7415 view.update(cx, |view, cx| {
7416 view.cancel(&Cancel, cx);
7417 assert_eq!(
7418 view.selections.display_ranges(cx),
7419 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
7420 );
7421 });
7422
7423 view.update(cx, |view, cx| {
7424 view.cancel(&Cancel, cx);
7425 assert_eq!(
7426 view.selections.display_ranges(cx),
7427 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7428 );
7429 });
7430 }
7431
7432 #[gpui::test]
7433 fn test_fold(cx: &mut gpui::MutableAppContext) {
7434 cx.set_global(Settings::test(cx));
7435 let buffer = MultiBuffer::build_simple(
7436 &"
7437 impl Foo {
7438 // Hello!
7439
7440 fn a() {
7441 1
7442 }
7443
7444 fn b() {
7445 2
7446 }
7447
7448 fn c() {
7449 3
7450 }
7451 }
7452 "
7453 .unindent(),
7454 cx,
7455 );
7456 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7457
7458 view.update(cx, |view, cx| {
7459 view.change_selections(None, cx, |s| {
7460 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7461 });
7462 view.fold(&Fold, cx);
7463 assert_eq!(
7464 view.display_text(cx),
7465 "
7466 impl Foo {
7467 // Hello!
7468
7469 fn a() {
7470 1
7471 }
7472
7473 fn b() {…
7474 }
7475
7476 fn c() {…
7477 }
7478 }
7479 "
7480 .unindent(),
7481 );
7482
7483 view.fold(&Fold, cx);
7484 assert_eq!(
7485 view.display_text(cx),
7486 "
7487 impl Foo {…
7488 }
7489 "
7490 .unindent(),
7491 );
7492
7493 view.unfold_lines(&UnfoldLines, cx);
7494 assert_eq!(
7495 view.display_text(cx),
7496 "
7497 impl Foo {
7498 // Hello!
7499
7500 fn a() {
7501 1
7502 }
7503
7504 fn b() {…
7505 }
7506
7507 fn c() {…
7508 }
7509 }
7510 "
7511 .unindent(),
7512 );
7513
7514 view.unfold_lines(&UnfoldLines, cx);
7515 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7516 });
7517 }
7518
7519 #[gpui::test]
7520 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7521 cx.set_global(Settings::test(cx));
7522 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7523 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7524
7525 buffer.update(cx, |buffer, cx| {
7526 buffer.edit(
7527 vec![
7528 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7529 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7530 ],
7531 None,
7532 cx,
7533 );
7534 });
7535
7536 view.update(cx, |view, cx| {
7537 assert_eq!(
7538 view.selections.display_ranges(cx),
7539 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7540 );
7541
7542 view.move_down(&MoveDown, cx);
7543 assert_eq!(
7544 view.selections.display_ranges(cx),
7545 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7546 );
7547
7548 view.move_right(&MoveRight, cx);
7549 assert_eq!(
7550 view.selections.display_ranges(cx),
7551 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7552 );
7553
7554 view.move_left(&MoveLeft, cx);
7555 assert_eq!(
7556 view.selections.display_ranges(cx),
7557 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7558 );
7559
7560 view.move_up(&MoveUp, cx);
7561 assert_eq!(
7562 view.selections.display_ranges(cx),
7563 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7564 );
7565
7566 view.move_to_end(&MoveToEnd, cx);
7567 assert_eq!(
7568 view.selections.display_ranges(cx),
7569 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7570 );
7571
7572 view.move_to_beginning(&MoveToBeginning, cx);
7573 assert_eq!(
7574 view.selections.display_ranges(cx),
7575 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7576 );
7577
7578 view.change_selections(None, cx, |s| {
7579 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7580 });
7581 view.select_to_beginning(&SelectToBeginning, cx);
7582 assert_eq!(
7583 view.selections.display_ranges(cx),
7584 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7585 );
7586
7587 view.select_to_end(&SelectToEnd, cx);
7588 assert_eq!(
7589 view.selections.display_ranges(cx),
7590 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7591 );
7592 });
7593 }
7594
7595 #[gpui::test]
7596 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7597 cx.set_global(Settings::test(cx));
7598 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7599 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7600
7601 assert_eq!('ⓐ'.len_utf8(), 3);
7602 assert_eq!('α'.len_utf8(), 2);
7603
7604 view.update(cx, |view, cx| {
7605 view.fold_ranges(
7606 vec![
7607 Point::new(0, 6)..Point::new(0, 12),
7608 Point::new(1, 2)..Point::new(1, 4),
7609 Point::new(2, 4)..Point::new(2, 8),
7610 ],
7611 cx,
7612 );
7613 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7614
7615 view.move_right(&MoveRight, cx);
7616 assert_eq!(
7617 view.selections.display_ranges(cx),
7618 &[empty_range(0, "ⓐ".len())]
7619 );
7620 view.move_right(&MoveRight, cx);
7621 assert_eq!(
7622 view.selections.display_ranges(cx),
7623 &[empty_range(0, "ⓐⓑ".len())]
7624 );
7625 view.move_right(&MoveRight, cx);
7626 assert_eq!(
7627 view.selections.display_ranges(cx),
7628 &[empty_range(0, "ⓐⓑ…".len())]
7629 );
7630
7631 view.move_down(&MoveDown, cx);
7632 assert_eq!(
7633 view.selections.display_ranges(cx),
7634 &[empty_range(1, "ab…".len())]
7635 );
7636 view.move_left(&MoveLeft, cx);
7637 assert_eq!(
7638 view.selections.display_ranges(cx),
7639 &[empty_range(1, "ab".len())]
7640 );
7641 view.move_left(&MoveLeft, cx);
7642 assert_eq!(
7643 view.selections.display_ranges(cx),
7644 &[empty_range(1, "a".len())]
7645 );
7646
7647 view.move_down(&MoveDown, cx);
7648 assert_eq!(
7649 view.selections.display_ranges(cx),
7650 &[empty_range(2, "α".len())]
7651 );
7652 view.move_right(&MoveRight, cx);
7653 assert_eq!(
7654 view.selections.display_ranges(cx),
7655 &[empty_range(2, "αβ".len())]
7656 );
7657 view.move_right(&MoveRight, cx);
7658 assert_eq!(
7659 view.selections.display_ranges(cx),
7660 &[empty_range(2, "αβ…".len())]
7661 );
7662 view.move_right(&MoveRight, cx);
7663 assert_eq!(
7664 view.selections.display_ranges(cx),
7665 &[empty_range(2, "αβ…ε".len())]
7666 );
7667
7668 view.move_up(&MoveUp, cx);
7669 assert_eq!(
7670 view.selections.display_ranges(cx),
7671 &[empty_range(1, "ab…e".len())]
7672 );
7673 view.move_up(&MoveUp, cx);
7674 assert_eq!(
7675 view.selections.display_ranges(cx),
7676 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7677 );
7678 view.move_left(&MoveLeft, cx);
7679 assert_eq!(
7680 view.selections.display_ranges(cx),
7681 &[empty_range(0, "ⓐⓑ…".len())]
7682 );
7683 view.move_left(&MoveLeft, cx);
7684 assert_eq!(
7685 view.selections.display_ranges(cx),
7686 &[empty_range(0, "ⓐⓑ".len())]
7687 );
7688 view.move_left(&MoveLeft, cx);
7689 assert_eq!(
7690 view.selections.display_ranges(cx),
7691 &[empty_range(0, "ⓐ".len())]
7692 );
7693 });
7694 }
7695
7696 #[gpui::test]
7697 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7698 cx.set_global(Settings::test(cx));
7699 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7700 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7701 view.update(cx, |view, cx| {
7702 view.change_selections(None, cx, |s| {
7703 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7704 });
7705 view.move_down(&MoveDown, cx);
7706 assert_eq!(
7707 view.selections.display_ranges(cx),
7708 &[empty_range(1, "abcd".len())]
7709 );
7710
7711 view.move_down(&MoveDown, cx);
7712 assert_eq!(
7713 view.selections.display_ranges(cx),
7714 &[empty_range(2, "αβγ".len())]
7715 );
7716
7717 view.move_down(&MoveDown, cx);
7718 assert_eq!(
7719 view.selections.display_ranges(cx),
7720 &[empty_range(3, "abcd".len())]
7721 );
7722
7723 view.move_down(&MoveDown, cx);
7724 assert_eq!(
7725 view.selections.display_ranges(cx),
7726 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7727 );
7728
7729 view.move_up(&MoveUp, cx);
7730 assert_eq!(
7731 view.selections.display_ranges(cx),
7732 &[empty_range(3, "abcd".len())]
7733 );
7734
7735 view.move_up(&MoveUp, cx);
7736 assert_eq!(
7737 view.selections.display_ranges(cx),
7738 &[empty_range(2, "αβγ".len())]
7739 );
7740 });
7741 }
7742
7743 #[gpui::test]
7744 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7745 cx.set_global(Settings::test(cx));
7746 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7747 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7748 view.update(cx, |view, cx| {
7749 view.change_selections(None, cx, |s| {
7750 s.select_display_ranges([
7751 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7752 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7753 ]);
7754 });
7755 });
7756
7757 view.update(cx, |view, cx| {
7758 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7759 assert_eq!(
7760 view.selections.display_ranges(cx),
7761 &[
7762 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7763 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7764 ]
7765 );
7766 });
7767
7768 view.update(cx, |view, cx| {
7769 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7770 assert_eq!(
7771 view.selections.display_ranges(cx),
7772 &[
7773 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7774 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7775 ]
7776 );
7777 });
7778
7779 view.update(cx, |view, cx| {
7780 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7781 assert_eq!(
7782 view.selections.display_ranges(cx),
7783 &[
7784 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7785 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7786 ]
7787 );
7788 });
7789
7790 view.update(cx, |view, cx| {
7791 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7792 assert_eq!(
7793 view.selections.display_ranges(cx),
7794 &[
7795 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7796 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7797 ]
7798 );
7799 });
7800
7801 // Moving to the end of line again is a no-op.
7802 view.update(cx, |view, cx| {
7803 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7804 assert_eq!(
7805 view.selections.display_ranges(cx),
7806 &[
7807 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7808 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7809 ]
7810 );
7811 });
7812
7813 view.update(cx, |view, cx| {
7814 view.move_left(&MoveLeft, cx);
7815 view.select_to_beginning_of_line(
7816 &SelectToBeginningOfLine {
7817 stop_at_soft_wraps: true,
7818 },
7819 cx,
7820 );
7821 assert_eq!(
7822 view.selections.display_ranges(cx),
7823 &[
7824 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7825 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7826 ]
7827 );
7828 });
7829
7830 view.update(cx, |view, cx| {
7831 view.select_to_beginning_of_line(
7832 &SelectToBeginningOfLine {
7833 stop_at_soft_wraps: true,
7834 },
7835 cx,
7836 );
7837 assert_eq!(
7838 view.selections.display_ranges(cx),
7839 &[
7840 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7841 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7842 ]
7843 );
7844 });
7845
7846 view.update(cx, |view, cx| {
7847 view.select_to_beginning_of_line(
7848 &SelectToBeginningOfLine {
7849 stop_at_soft_wraps: true,
7850 },
7851 cx,
7852 );
7853 assert_eq!(
7854 view.selections.display_ranges(cx),
7855 &[
7856 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7857 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7858 ]
7859 );
7860 });
7861
7862 view.update(cx, |view, cx| {
7863 view.select_to_end_of_line(
7864 &SelectToEndOfLine {
7865 stop_at_soft_wraps: true,
7866 },
7867 cx,
7868 );
7869 assert_eq!(
7870 view.selections.display_ranges(cx),
7871 &[
7872 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7873 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7874 ]
7875 );
7876 });
7877
7878 view.update(cx, |view, cx| {
7879 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7880 assert_eq!(view.display_text(cx), "ab\n de");
7881 assert_eq!(
7882 view.selections.display_ranges(cx),
7883 &[
7884 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7885 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7886 ]
7887 );
7888 });
7889
7890 view.update(cx, |view, cx| {
7891 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7892 assert_eq!(view.display_text(cx), "\n");
7893 assert_eq!(
7894 view.selections.display_ranges(cx),
7895 &[
7896 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7897 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7898 ]
7899 );
7900 });
7901 }
7902
7903 #[gpui::test]
7904 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7905 cx.set_global(Settings::test(cx));
7906 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7907 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7908 view.update(cx, |view, cx| {
7909 view.change_selections(None, cx, |s| {
7910 s.select_display_ranges([
7911 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7912 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7913 ])
7914 });
7915
7916 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7917 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7918
7919 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7920 assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx);
7921
7922 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7923 assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx);
7924
7925 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7926 assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7927
7928 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7929 assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx);
7930
7931 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7932 assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx);
7933
7934 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7935 assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7936
7937 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7938 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7939
7940 view.move_right(&MoveRight, cx);
7941 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7942 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7943
7944 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7945 assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx);
7946
7947 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7948 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7949 });
7950 }
7951
7952 #[gpui::test]
7953 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7954 cx.set_global(Settings::test(cx));
7955 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7956 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7957
7958 view.update(cx, |view, cx| {
7959 view.set_wrap_width(Some(140.), cx);
7960 assert_eq!(
7961 view.display_text(cx),
7962 "use one::{\n two::three::\n four::five\n};"
7963 );
7964
7965 view.change_selections(None, cx, |s| {
7966 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7967 });
7968
7969 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7970 assert_eq!(
7971 view.selections.display_ranges(cx),
7972 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7973 );
7974
7975 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7976 assert_eq!(
7977 view.selections.display_ranges(cx),
7978 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7979 );
7980
7981 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7982 assert_eq!(
7983 view.selections.display_ranges(cx),
7984 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7985 );
7986
7987 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7988 assert_eq!(
7989 view.selections.display_ranges(cx),
7990 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7991 );
7992
7993 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7994 assert_eq!(
7995 view.selections.display_ranges(cx),
7996 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7997 );
7998
7999 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
8000 assert_eq!(
8001 view.selections.display_ranges(cx),
8002 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
8003 );
8004 });
8005 }
8006
8007 #[gpui::test]
8008 async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
8009 let mut cx = EditorTestContext::new(cx);
8010 cx.set_state("one «two threeˇ» four");
8011 cx.update_editor(|editor, cx| {
8012 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
8013 assert_eq!(editor.text(cx), " four");
8014 });
8015 }
8016
8017 #[gpui::test]
8018 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
8019 cx.set_global(Settings::test(cx));
8020 let buffer = MultiBuffer::build_simple("one two three four", cx);
8021 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
8022
8023 view.update(cx, |view, cx| {
8024 view.change_selections(None, cx, |s| {
8025 s.select_display_ranges([
8026 // an empty selection - the preceding word fragment is deleted
8027 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8028 // characters selected - they are deleted
8029 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
8030 ])
8031 });
8032 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
8033 });
8034
8035 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
8036
8037 view.update(cx, |view, cx| {
8038 view.change_selections(None, cx, |s| {
8039 s.select_display_ranges([
8040 // an empty selection - the following word fragment is deleted
8041 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8042 // characters selected - they are deleted
8043 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
8044 ])
8045 });
8046 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
8047 });
8048
8049 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
8050 }
8051
8052 #[gpui::test]
8053 fn test_newline(cx: &mut gpui::MutableAppContext) {
8054 cx.set_global(Settings::test(cx));
8055 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
8056 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
8057
8058 view.update(cx, |view, cx| {
8059 view.change_selections(None, cx, |s| {
8060 s.select_display_ranges([
8061 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8062 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8063 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
8064 ])
8065 });
8066
8067 view.newline(&Newline, cx);
8068 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
8069 });
8070 }
8071
8072 #[gpui::test]
8073 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
8074 cx.set_global(Settings::test(cx));
8075 let buffer = MultiBuffer::build_simple(
8076 "
8077 a
8078 b(
8079 X
8080 )
8081 c(
8082 X
8083 )
8084 "
8085 .unindent()
8086 .as_str(),
8087 cx,
8088 );
8089
8090 let (_, editor) = cx.add_window(Default::default(), |cx| {
8091 let mut editor = build_editor(buffer.clone(), cx);
8092 editor.change_selections(None, cx, |s| {
8093 s.select_ranges([
8094 Point::new(2, 4)..Point::new(2, 5),
8095 Point::new(5, 4)..Point::new(5, 5),
8096 ])
8097 });
8098 editor
8099 });
8100
8101 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8102 buffer.update(cx, |buffer, cx| {
8103 buffer.edit(
8104 [
8105 (Point::new(1, 2)..Point::new(3, 0), ""),
8106 (Point::new(4, 2)..Point::new(6, 0), ""),
8107 ],
8108 None,
8109 cx,
8110 );
8111 assert_eq!(
8112 buffer.read(cx).text(),
8113 "
8114 a
8115 b()
8116 c()
8117 "
8118 .unindent()
8119 );
8120 });
8121
8122 editor.update(cx, |editor, cx| {
8123 assert_eq!(
8124 editor.selections.ranges(cx),
8125 &[
8126 Point::new(1, 2)..Point::new(1, 2),
8127 Point::new(2, 2)..Point::new(2, 2),
8128 ],
8129 );
8130
8131 editor.newline(&Newline, cx);
8132 assert_eq!(
8133 editor.text(cx),
8134 "
8135 a
8136 b(
8137 )
8138 c(
8139 )
8140 "
8141 .unindent()
8142 );
8143
8144 // The selections are moved after the inserted newlines
8145 assert_eq!(
8146 editor.selections.ranges(cx),
8147 &[
8148 Point::new(2, 0)..Point::new(2, 0),
8149 Point::new(4, 0)..Point::new(4, 0),
8150 ],
8151 );
8152 });
8153 }
8154
8155 #[gpui::test]
8156 async fn test_newline_below(cx: &mut gpui::TestAppContext) {
8157 let mut cx = EditorTestContext::new(cx);
8158 cx.update(|cx| {
8159 cx.update_global::<Settings, _, _>(|settings, _| {
8160 settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
8161 });
8162 });
8163
8164 let language = Arc::new(
8165 Language::new(
8166 LanguageConfig::default(),
8167 Some(tree_sitter_rust::language()),
8168 )
8169 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8170 .unwrap(),
8171 );
8172 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8173
8174 cx.set_state(indoc! {"
8175 const a: ˇA = (
8176 (ˇ
8177 «const_functionˇ»(ˇ),
8178 so«mˇ»et«hˇ»ing_ˇelse,ˇ
8179 )ˇ
8180 ˇ);ˇ
8181 "});
8182 cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
8183 cx.assert_editor_state(indoc! {"
8184 const a: A = (
8185 ˇ
8186 (
8187 ˇ
8188 const_function(),
8189 ˇ
8190 ˇ
8191 something_else,
8192 ˇ
8193 ˇ
8194 ˇ
8195 ˇ
8196 )
8197 ˇ
8198 );
8199 ˇ
8200 ˇ
8201 "});
8202 }
8203
8204 #[gpui::test]
8205 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
8206 cx.set_global(Settings::test(cx));
8207 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
8208 let (_, editor) = cx.add_window(Default::default(), |cx| {
8209 let mut editor = build_editor(buffer.clone(), cx);
8210 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
8211 editor
8212 });
8213
8214 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8215 buffer.update(cx, |buffer, cx| {
8216 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
8217 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
8218 });
8219
8220 editor.update(cx, |editor, cx| {
8221 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
8222
8223 editor.insert("Z", cx);
8224 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
8225
8226 // The selections are moved after the inserted characters
8227 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
8228 });
8229 }
8230
8231 #[gpui::test]
8232 async fn test_tab(cx: &mut gpui::TestAppContext) {
8233 let mut cx = EditorTestContext::new(cx);
8234 cx.update(|cx| {
8235 cx.update_global::<Settings, _, _>(|settings, _| {
8236 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
8237 });
8238 });
8239 cx.set_state(indoc! {"
8240 ˇabˇc
8241 ˇ🏀ˇ🏀ˇefg
8242 dˇ
8243 "});
8244 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8245 cx.assert_editor_state(indoc! {"
8246 ˇab ˇc
8247 ˇ🏀 ˇ🏀 ˇefg
8248 d ˇ
8249 "});
8250
8251 cx.set_state(indoc! {"
8252 a
8253 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8254 "});
8255 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8256 cx.assert_editor_state(indoc! {"
8257 a
8258 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8259 "});
8260 }
8261
8262 #[gpui::test]
8263 async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
8264 let mut cx = EditorTestContext::new(cx);
8265 let language = Arc::new(
8266 Language::new(
8267 LanguageConfig::default(),
8268 Some(tree_sitter_rust::language()),
8269 )
8270 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8271 .unwrap(),
8272 );
8273 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8274
8275 // cursors that are already at the suggested indent level insert
8276 // a soft tab. cursors that are to the left of the suggested indent
8277 // auto-indent their line.
8278 cx.set_state(indoc! {"
8279 ˇ
8280 const a: B = (
8281 c(
8282 d(
8283 ˇ
8284 )
8285 ˇ
8286 ˇ )
8287 );
8288 "});
8289 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8290 cx.assert_editor_state(indoc! {"
8291 ˇ
8292 const a: B = (
8293 c(
8294 d(
8295 ˇ
8296 )
8297 ˇ
8298 ˇ)
8299 );
8300 "});
8301
8302 // handle auto-indent when there are multiple cursors on the same line
8303 cx.set_state(indoc! {"
8304 const a: B = (
8305 c(
8306 ˇ ˇ
8307 ˇ )
8308 );
8309 "});
8310 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8311 cx.assert_editor_state(indoc! {"
8312 const a: B = (
8313 c(
8314 ˇ
8315 ˇ)
8316 );
8317 "});
8318 }
8319
8320 #[gpui::test]
8321 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
8322 let mut cx = EditorTestContext::new(cx);
8323
8324 cx.set_state(indoc! {"
8325 «oneˇ» «twoˇ»
8326 three
8327 four
8328 "});
8329 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8330 cx.assert_editor_state(indoc! {"
8331 «oneˇ» «twoˇ»
8332 three
8333 four
8334 "});
8335
8336 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8337 cx.assert_editor_state(indoc! {"
8338 «oneˇ» «twoˇ»
8339 three
8340 four
8341 "});
8342
8343 // select across line ending
8344 cx.set_state(indoc! {"
8345 one two
8346 t«hree
8347 ˇ» four
8348 "});
8349 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8350 cx.assert_editor_state(indoc! {"
8351 one two
8352 t«hree
8353 ˇ» four
8354 "});
8355
8356 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8357 cx.assert_editor_state(indoc! {"
8358 one two
8359 t«hree
8360 ˇ» four
8361 "});
8362
8363 // Ensure that indenting/outdenting works when the cursor is at column 0.
8364 cx.set_state(indoc! {"
8365 one two
8366 ˇthree
8367 four
8368 "});
8369 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8370 cx.assert_editor_state(indoc! {"
8371 one two
8372 ˇthree
8373 four
8374 "});
8375
8376 cx.set_state(indoc! {"
8377 one two
8378 ˇ three
8379 four
8380 "});
8381 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8382 cx.assert_editor_state(indoc! {"
8383 one two
8384 ˇthree
8385 four
8386 "});
8387 }
8388
8389 #[gpui::test]
8390 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8391 let mut cx = EditorTestContext::new(cx);
8392 cx.update(|cx| {
8393 cx.update_global::<Settings, _, _>(|settings, _| {
8394 settings.editor_overrides.hard_tabs = Some(true);
8395 });
8396 });
8397
8398 // select two ranges on one line
8399 cx.set_state(indoc! {"
8400 «oneˇ» «twoˇ»
8401 three
8402 four
8403 "});
8404 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8405 cx.assert_editor_state(indoc! {"
8406 \t«oneˇ» «twoˇ»
8407 three
8408 four
8409 "});
8410 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8411 cx.assert_editor_state(indoc! {"
8412 \t\t«oneˇ» «twoˇ»
8413 three
8414 four
8415 "});
8416 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8417 cx.assert_editor_state(indoc! {"
8418 \t«oneˇ» «twoˇ»
8419 three
8420 four
8421 "});
8422 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8423 cx.assert_editor_state(indoc! {"
8424 «oneˇ» «twoˇ»
8425 three
8426 four
8427 "});
8428
8429 // select across a line ending
8430 cx.set_state(indoc! {"
8431 one two
8432 t«hree
8433 ˇ»four
8434 "});
8435 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8436 cx.assert_editor_state(indoc! {"
8437 one two
8438 \tt«hree
8439 ˇ»four
8440 "});
8441 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8442 cx.assert_editor_state(indoc! {"
8443 one two
8444 \t\tt«hree
8445 ˇ»four
8446 "});
8447 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8448 cx.assert_editor_state(indoc! {"
8449 one two
8450 \tt«hree
8451 ˇ»four
8452 "});
8453 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8454 cx.assert_editor_state(indoc! {"
8455 one two
8456 t«hree
8457 ˇ»four
8458 "});
8459
8460 // Ensure that indenting/outdenting works when the cursor is at column 0.
8461 cx.set_state(indoc! {"
8462 one two
8463 ˇthree
8464 four
8465 "});
8466 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8467 cx.assert_editor_state(indoc! {"
8468 one two
8469 ˇthree
8470 four
8471 "});
8472 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8473 cx.assert_editor_state(indoc! {"
8474 one two
8475 \tˇthree
8476 four
8477 "});
8478 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8479 cx.assert_editor_state(indoc! {"
8480 one two
8481 ˇthree
8482 four
8483 "});
8484 }
8485
8486 #[gpui::test]
8487 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8488 cx.set_global(
8489 Settings::test(cx)
8490 .with_language_defaults(
8491 "TOML",
8492 EditorSettings {
8493 tab_size: Some(2.try_into().unwrap()),
8494 ..Default::default()
8495 },
8496 )
8497 .with_language_defaults(
8498 "Rust",
8499 EditorSettings {
8500 tab_size: Some(4.try_into().unwrap()),
8501 ..Default::default()
8502 },
8503 ),
8504 );
8505 let toml_language = Arc::new(Language::new(
8506 LanguageConfig {
8507 name: "TOML".into(),
8508 ..Default::default()
8509 },
8510 None,
8511 ));
8512 let rust_language = Arc::new(Language::new(
8513 LanguageConfig {
8514 name: "Rust".into(),
8515 ..Default::default()
8516 },
8517 None,
8518 ));
8519
8520 let toml_buffer = cx
8521 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8522 let rust_buffer = cx.add_model(|cx| {
8523 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8524 });
8525 let multibuffer = cx.add_model(|cx| {
8526 let mut multibuffer = MultiBuffer::new(0);
8527 multibuffer.push_excerpts(
8528 toml_buffer.clone(),
8529 [ExcerptRange {
8530 context: Point::new(0, 0)..Point::new(2, 0),
8531 primary: None,
8532 }],
8533 cx,
8534 );
8535 multibuffer.push_excerpts(
8536 rust_buffer.clone(),
8537 [ExcerptRange {
8538 context: Point::new(0, 0)..Point::new(1, 0),
8539 primary: None,
8540 }],
8541 cx,
8542 );
8543 multibuffer
8544 });
8545
8546 cx.add_window(Default::default(), |cx| {
8547 let mut editor = build_editor(multibuffer, cx);
8548
8549 assert_eq!(
8550 editor.text(cx),
8551 indoc! {"
8552 a = 1
8553 b = 2
8554
8555 const c: usize = 3;
8556 "}
8557 );
8558
8559 select_ranges(
8560 &mut editor,
8561 indoc! {"
8562 «aˇ» = 1
8563 b = 2
8564
8565 «const c:ˇ» usize = 3;
8566 "},
8567 cx,
8568 );
8569
8570 editor.tab(&Tab, cx);
8571 assert_text_with_selections(
8572 &mut editor,
8573 indoc! {"
8574 «aˇ» = 1
8575 b = 2
8576
8577 «const c:ˇ» usize = 3;
8578 "},
8579 cx,
8580 );
8581 editor.tab_prev(&TabPrev, cx);
8582 assert_text_with_selections(
8583 &mut editor,
8584 indoc! {"
8585 «aˇ» = 1
8586 b = 2
8587
8588 «const c:ˇ» usize = 3;
8589 "},
8590 cx,
8591 );
8592
8593 editor
8594 });
8595 }
8596
8597 #[gpui::test]
8598 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8599 let mut cx = EditorTestContext::new(cx);
8600
8601 // Basic backspace
8602 cx.set_state(indoc! {"
8603 onˇe two three
8604 fou«rˇ» five six
8605 seven «ˇeight nine
8606 »ten
8607 "});
8608 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8609 cx.assert_editor_state(indoc! {"
8610 oˇe two three
8611 fouˇ five six
8612 seven ˇten
8613 "});
8614
8615 // Test backspace inside and around indents
8616 cx.set_state(indoc! {"
8617 zero
8618 ˇone
8619 ˇtwo
8620 ˇ ˇ ˇ three
8621 ˇ ˇ four
8622 "});
8623 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8624 cx.assert_editor_state(indoc! {"
8625 zero
8626 ˇone
8627 ˇtwo
8628 ˇ threeˇ four
8629 "});
8630
8631 // Test backspace with line_mode set to true
8632 cx.update_editor(|e, _| e.selections.line_mode = true);
8633 cx.set_state(indoc! {"
8634 The ˇquick ˇbrown
8635 fox jumps over
8636 the lazy dog
8637 ˇThe qu«ick bˇ»rown"});
8638 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8639 cx.assert_editor_state(indoc! {"
8640 ˇfox jumps over
8641 the lazy dogˇ"});
8642 }
8643
8644 #[gpui::test]
8645 async fn test_delete(cx: &mut gpui::TestAppContext) {
8646 let mut cx = EditorTestContext::new(cx);
8647
8648 cx.set_state(indoc! {"
8649 onˇe two three
8650 fou«rˇ» five six
8651 seven «ˇeight nine
8652 »ten
8653 "});
8654 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8655 cx.assert_editor_state(indoc! {"
8656 onˇ two three
8657 fouˇ five six
8658 seven ˇten
8659 "});
8660
8661 // Test backspace with line_mode set to true
8662 cx.update_editor(|e, _| e.selections.line_mode = true);
8663 cx.set_state(indoc! {"
8664 The ˇquick ˇbrown
8665 fox «ˇjum»ps over
8666 the lazy dog
8667 ˇThe qu«ick bˇ»rown"});
8668 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8669 cx.assert_editor_state("ˇthe lazy dogˇ");
8670 }
8671
8672 #[gpui::test]
8673 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8674 cx.set_global(Settings::test(cx));
8675 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8676 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8677 view.update(cx, |view, cx| {
8678 view.change_selections(None, cx, |s| {
8679 s.select_display_ranges([
8680 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8681 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8682 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8683 ])
8684 });
8685 view.delete_line(&DeleteLine, cx);
8686 assert_eq!(view.display_text(cx), "ghi");
8687 assert_eq!(
8688 view.selections.display_ranges(cx),
8689 vec![
8690 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8691 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8692 ]
8693 );
8694 });
8695
8696 cx.set_global(Settings::test(cx));
8697 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8698 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8699 view.update(cx, |view, cx| {
8700 view.change_selections(None, cx, |s| {
8701 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8702 });
8703 view.delete_line(&DeleteLine, cx);
8704 assert_eq!(view.display_text(cx), "ghi\n");
8705 assert_eq!(
8706 view.selections.display_ranges(cx),
8707 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8708 );
8709 });
8710 }
8711
8712 #[gpui::test]
8713 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8714 cx.set_global(Settings::test(cx));
8715 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8716 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8717 view.update(cx, |view, cx| {
8718 view.change_selections(None, cx, |s| {
8719 s.select_display_ranges([
8720 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8721 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8722 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8723 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8724 ])
8725 });
8726 view.duplicate_line(&DuplicateLine, cx);
8727 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8728 assert_eq!(
8729 view.selections.display_ranges(cx),
8730 vec![
8731 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8732 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8733 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8734 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8735 ]
8736 );
8737 });
8738
8739 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8740 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8741 view.update(cx, |view, cx| {
8742 view.change_selections(None, cx, |s| {
8743 s.select_display_ranges([
8744 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8745 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8746 ])
8747 });
8748 view.duplicate_line(&DuplicateLine, cx);
8749 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8750 assert_eq!(
8751 view.selections.display_ranges(cx),
8752 vec![
8753 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8754 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8755 ]
8756 );
8757 });
8758 }
8759
8760 #[gpui::test]
8761 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8762 cx.set_global(Settings::test(cx));
8763 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8764 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8765 view.update(cx, |view, cx| {
8766 view.fold_ranges(
8767 vec![
8768 Point::new(0, 2)..Point::new(1, 2),
8769 Point::new(2, 3)..Point::new(4, 1),
8770 Point::new(7, 0)..Point::new(8, 4),
8771 ],
8772 cx,
8773 );
8774 view.change_selections(None, cx, |s| {
8775 s.select_display_ranges([
8776 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8777 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8778 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8779 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8780 ])
8781 });
8782 assert_eq!(
8783 view.display_text(cx),
8784 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8785 );
8786
8787 view.move_line_up(&MoveLineUp, cx);
8788 assert_eq!(
8789 view.display_text(cx),
8790 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8791 );
8792 assert_eq!(
8793 view.selections.display_ranges(cx),
8794 vec![
8795 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8796 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8797 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8798 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8799 ]
8800 );
8801 });
8802
8803 view.update(cx, |view, cx| {
8804 view.move_line_down(&MoveLineDown, cx);
8805 assert_eq!(
8806 view.display_text(cx),
8807 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8808 );
8809 assert_eq!(
8810 view.selections.display_ranges(cx),
8811 vec![
8812 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8813 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8814 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8815 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8816 ]
8817 );
8818 });
8819
8820 view.update(cx, |view, cx| {
8821 view.move_line_down(&MoveLineDown, cx);
8822 assert_eq!(
8823 view.display_text(cx),
8824 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8825 );
8826 assert_eq!(
8827 view.selections.display_ranges(cx),
8828 vec![
8829 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8830 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8831 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8832 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8833 ]
8834 );
8835 });
8836
8837 view.update(cx, |view, cx| {
8838 view.move_line_up(&MoveLineUp, cx);
8839 assert_eq!(
8840 view.display_text(cx),
8841 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8842 );
8843 assert_eq!(
8844 view.selections.display_ranges(cx),
8845 vec![
8846 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8847 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8848 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8849 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8850 ]
8851 );
8852 });
8853 }
8854
8855 #[gpui::test]
8856 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8857 cx.set_global(Settings::test(cx));
8858 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8859 let snapshot = buffer.read(cx).snapshot(cx);
8860 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8861 editor.update(cx, |editor, cx| {
8862 editor.insert_blocks(
8863 [BlockProperties {
8864 style: BlockStyle::Fixed,
8865 position: snapshot.anchor_after(Point::new(2, 0)),
8866 disposition: BlockDisposition::Below,
8867 height: 1,
8868 render: Arc::new(|_| Empty::new().boxed()),
8869 }],
8870 cx,
8871 );
8872 editor.change_selections(None, cx, |s| {
8873 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8874 });
8875 editor.move_line_down(&MoveLineDown, cx);
8876 });
8877 }
8878
8879 #[gpui::test]
8880 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8881 cx.set_global(Settings::test(cx));
8882
8883 _ = cx
8884 .add_window(Default::default(), |cx| {
8885 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8886
8887 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8888 editor.transpose(&Default::default(), cx);
8889 assert_eq!(editor.text(cx), "bac");
8890 assert_eq!(editor.selections.ranges(cx), [2..2]);
8891
8892 editor.transpose(&Default::default(), cx);
8893 assert_eq!(editor.text(cx), "bca");
8894 assert_eq!(editor.selections.ranges(cx), [3..3]);
8895
8896 editor.transpose(&Default::default(), cx);
8897 assert_eq!(editor.text(cx), "bac");
8898 assert_eq!(editor.selections.ranges(cx), [3..3]);
8899
8900 editor
8901 })
8902 .1;
8903
8904 _ = cx
8905 .add_window(Default::default(), |cx| {
8906 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8907
8908 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8909 editor.transpose(&Default::default(), cx);
8910 assert_eq!(editor.text(cx), "acb\nde");
8911 assert_eq!(editor.selections.ranges(cx), [3..3]);
8912
8913 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8914 editor.transpose(&Default::default(), cx);
8915 assert_eq!(editor.text(cx), "acbd\ne");
8916 assert_eq!(editor.selections.ranges(cx), [5..5]);
8917
8918 editor.transpose(&Default::default(), cx);
8919 assert_eq!(editor.text(cx), "acbde\n");
8920 assert_eq!(editor.selections.ranges(cx), [6..6]);
8921
8922 editor.transpose(&Default::default(), cx);
8923 assert_eq!(editor.text(cx), "acbd\ne");
8924 assert_eq!(editor.selections.ranges(cx), [6..6]);
8925
8926 editor
8927 })
8928 .1;
8929
8930 _ = cx
8931 .add_window(Default::default(), |cx| {
8932 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8933
8934 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8935 editor.transpose(&Default::default(), cx);
8936 assert_eq!(editor.text(cx), "bacd\ne");
8937 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8938
8939 editor.transpose(&Default::default(), cx);
8940 assert_eq!(editor.text(cx), "bcade\n");
8941 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8942
8943 editor.transpose(&Default::default(), cx);
8944 assert_eq!(editor.text(cx), "bcda\ne");
8945 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8946
8947 editor.transpose(&Default::default(), cx);
8948 assert_eq!(editor.text(cx), "bcade\n");
8949 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8950
8951 editor.transpose(&Default::default(), cx);
8952 assert_eq!(editor.text(cx), "bcaed\n");
8953 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8954
8955 editor
8956 })
8957 .1;
8958
8959 _ = cx
8960 .add_window(Default::default(), |cx| {
8961 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8962
8963 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8964 editor.transpose(&Default::default(), cx);
8965 assert_eq!(editor.text(cx), "🏀🍐✋");
8966 assert_eq!(editor.selections.ranges(cx), [8..8]);
8967
8968 editor.transpose(&Default::default(), cx);
8969 assert_eq!(editor.text(cx), "🏀✋🍐");
8970 assert_eq!(editor.selections.ranges(cx), [11..11]);
8971
8972 editor.transpose(&Default::default(), cx);
8973 assert_eq!(editor.text(cx), "🏀🍐✋");
8974 assert_eq!(editor.selections.ranges(cx), [11..11]);
8975
8976 editor
8977 })
8978 .1;
8979 }
8980
8981 #[gpui::test]
8982 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8983 let mut cx = EditorTestContext::new(cx);
8984
8985 cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
8986 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8987 cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
8988
8989 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8990 cx.set_state("two ˇfour ˇsix ˇ");
8991 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8992 cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
8993
8994 // Paste again but with only two cursors. Since the number of cursors doesn't
8995 // match the number of slices in the clipboard, the entire clipboard text
8996 // is pasted at each cursor.
8997 cx.set_state("ˇtwo one✅ four three six five ˇ");
8998 cx.update_editor(|e, cx| {
8999 e.handle_input("( ", cx);
9000 e.paste(&Paste, cx);
9001 e.handle_input(") ", cx);
9002 });
9003 cx.assert_editor_state(indoc! {"
9004 ( one✅
9005 three
9006 five ) ˇtwo one✅ four three six five ( one✅
9007 three
9008 five ) ˇ"});
9009
9010 // Cut with three selections, one of which is full-line.
9011 cx.set_state(indoc! {"
9012 1«2ˇ»3
9013 4ˇ567
9014 «8ˇ»9"});
9015 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9016 cx.assert_editor_state(indoc! {"
9017 1ˇ3
9018 ˇ9"});
9019
9020 // Paste with three selections, noticing how the copied selection that was full-line
9021 // gets inserted before the second cursor.
9022 cx.set_state(indoc! {"
9023 1ˇ3
9024 9ˇ
9025 «oˇ»ne"});
9026 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9027 cx.assert_editor_state(indoc! {"
9028 12ˇ3
9029 4567
9030 9ˇ
9031 8ˇne"});
9032
9033 // Copy with a single cursor only, which writes the whole line into the clipboard.
9034 cx.set_state(indoc! {"
9035 The quick brown
9036 fox juˇmps over
9037 the lazy dog"});
9038 cx.update_editor(|e, cx| e.copy(&Copy, cx));
9039 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
9040
9041 // Paste with three selections, noticing how the copied full-line selection is inserted
9042 // before the empty selections but replaces the selection that is non-empty.
9043 cx.set_state(indoc! {"
9044 Tˇhe quick brown
9045 «foˇ»x jumps over
9046 tˇhe lazy dog"});
9047 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9048 cx.assert_editor_state(indoc! {"
9049 fox jumps over
9050 Tˇhe quick brown
9051 fox jumps over
9052 ˇx jumps over
9053 fox jumps over
9054 tˇhe lazy dog"});
9055 }
9056
9057 #[gpui::test]
9058 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
9059 let mut cx = EditorTestContext::new(cx);
9060 let language = Arc::new(Language::new(
9061 LanguageConfig::default(),
9062 Some(tree_sitter_rust::language()),
9063 ));
9064 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
9065
9066 // Cut an indented block, without the leading whitespace.
9067 cx.set_state(indoc! {"
9068 const a: B = (
9069 c(),
9070 «d(
9071 e,
9072 f
9073 )ˇ»
9074 );
9075 "});
9076 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9077 cx.assert_editor_state(indoc! {"
9078 const a: B = (
9079 c(),
9080 ˇ
9081 );
9082 "});
9083
9084 // Paste it at the same position.
9085 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9086 cx.assert_editor_state(indoc! {"
9087 const a: B = (
9088 c(),
9089 d(
9090 e,
9091 f
9092 )ˇ
9093 );
9094 "});
9095
9096 // Paste it at a line with a lower indent level.
9097 cx.set_state(indoc! {"
9098 ˇ
9099 const a: B = (
9100 c(),
9101 );
9102 "});
9103 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9104 cx.assert_editor_state(indoc! {"
9105 d(
9106 e,
9107 f
9108 )ˇ
9109 const a: B = (
9110 c(),
9111 );
9112 "});
9113
9114 // Cut an indented block, with the leading whitespace.
9115 cx.set_state(indoc! {"
9116 const a: B = (
9117 c(),
9118 « d(
9119 e,
9120 f
9121 )
9122 ˇ»);
9123 "});
9124 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9125 cx.assert_editor_state(indoc! {"
9126 const a: B = (
9127 c(),
9128 ˇ);
9129 "});
9130
9131 // Paste it at the same position.
9132 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9133 cx.assert_editor_state(indoc! {"
9134 const a: B = (
9135 c(),
9136 d(
9137 e,
9138 f
9139 )
9140 ˇ);
9141 "});
9142
9143 // Paste it at a line with a higher indent level.
9144 cx.set_state(indoc! {"
9145 const a: B = (
9146 c(),
9147 d(
9148 e,
9149 fˇ
9150 )
9151 );
9152 "});
9153 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9154 cx.assert_editor_state(indoc! {"
9155 const a: B = (
9156 c(),
9157 d(
9158 e,
9159 f d(
9160 e,
9161 f
9162 )
9163 ˇ
9164 )
9165 );
9166 "});
9167 }
9168
9169 #[gpui::test]
9170 fn test_select_all(cx: &mut gpui::MutableAppContext) {
9171 cx.set_global(Settings::test(cx));
9172 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
9173 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9174 view.update(cx, |view, cx| {
9175 view.select_all(&SelectAll, cx);
9176 assert_eq!(
9177 view.selections.display_ranges(cx),
9178 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
9179 );
9180 });
9181 }
9182
9183 #[gpui::test]
9184 fn test_select_line(cx: &mut gpui::MutableAppContext) {
9185 cx.set_global(Settings::test(cx));
9186 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
9187 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9188 view.update(cx, |view, cx| {
9189 view.change_selections(None, cx, |s| {
9190 s.select_display_ranges([
9191 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9192 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9193 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9194 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
9195 ])
9196 });
9197 view.select_line(&SelectLine, cx);
9198 assert_eq!(
9199 view.selections.display_ranges(cx),
9200 vec![
9201 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
9202 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
9203 ]
9204 );
9205 });
9206
9207 view.update(cx, |view, cx| {
9208 view.select_line(&SelectLine, cx);
9209 assert_eq!(
9210 view.selections.display_ranges(cx),
9211 vec![
9212 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
9213 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
9214 ]
9215 );
9216 });
9217
9218 view.update(cx, |view, cx| {
9219 view.select_line(&SelectLine, cx);
9220 assert_eq!(
9221 view.selections.display_ranges(cx),
9222 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
9223 );
9224 });
9225 }
9226
9227 #[gpui::test]
9228 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
9229 cx.set_global(Settings::test(cx));
9230 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
9231 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9232 view.update(cx, |view, cx| {
9233 view.fold_ranges(
9234 vec![
9235 Point::new(0, 2)..Point::new(1, 2),
9236 Point::new(2, 3)..Point::new(4, 1),
9237 Point::new(7, 0)..Point::new(8, 4),
9238 ],
9239 cx,
9240 );
9241 view.change_selections(None, cx, |s| {
9242 s.select_display_ranges([
9243 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9244 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9245 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9246 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9247 ])
9248 });
9249 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
9250 });
9251
9252 view.update(cx, |view, cx| {
9253 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9254 assert_eq!(
9255 view.display_text(cx),
9256 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9257 );
9258 assert_eq!(
9259 view.selections.display_ranges(cx),
9260 [
9261 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9262 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9263 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9264 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9265 ]
9266 );
9267 });
9268
9269 view.update(cx, |view, cx| {
9270 view.change_selections(None, cx, |s| {
9271 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9272 });
9273 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9274 assert_eq!(
9275 view.display_text(cx),
9276 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9277 );
9278 assert_eq!(
9279 view.selections.display_ranges(cx),
9280 [
9281 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9282 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9283 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9284 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9285 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9286 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9287 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9288 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9289 ]
9290 );
9291 });
9292 }
9293
9294 #[gpui::test]
9295 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9296 cx.set_global(Settings::test(cx));
9297 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9298 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9299
9300 view.update(cx, |view, cx| {
9301 view.change_selections(None, cx, |s| {
9302 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9303 });
9304 });
9305 view.update(cx, |view, cx| {
9306 view.add_selection_above(&AddSelectionAbove, cx);
9307 assert_eq!(
9308 view.selections.display_ranges(cx),
9309 vec![
9310 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9311 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9312 ]
9313 );
9314 });
9315
9316 view.update(cx, |view, cx| {
9317 view.add_selection_above(&AddSelectionAbove, cx);
9318 assert_eq!(
9319 view.selections.display_ranges(cx),
9320 vec![
9321 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9322 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9323 ]
9324 );
9325 });
9326
9327 view.update(cx, |view, cx| {
9328 view.add_selection_below(&AddSelectionBelow, cx);
9329 assert_eq!(
9330 view.selections.display_ranges(cx),
9331 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9332 );
9333
9334 view.undo_selection(&UndoSelection, cx);
9335 assert_eq!(
9336 view.selections.display_ranges(cx),
9337 vec![
9338 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9339 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9340 ]
9341 );
9342
9343 view.redo_selection(&RedoSelection, cx);
9344 assert_eq!(
9345 view.selections.display_ranges(cx),
9346 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9347 );
9348 });
9349
9350 view.update(cx, |view, cx| {
9351 view.add_selection_below(&AddSelectionBelow, cx);
9352 assert_eq!(
9353 view.selections.display_ranges(cx),
9354 vec![
9355 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9356 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9357 ]
9358 );
9359 });
9360
9361 view.update(cx, |view, cx| {
9362 view.add_selection_below(&AddSelectionBelow, cx);
9363 assert_eq!(
9364 view.selections.display_ranges(cx),
9365 vec![
9366 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9367 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9368 ]
9369 );
9370 });
9371
9372 view.update(cx, |view, cx| {
9373 view.change_selections(None, cx, |s| {
9374 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9375 });
9376 });
9377 view.update(cx, |view, cx| {
9378 view.add_selection_below(&AddSelectionBelow, cx);
9379 assert_eq!(
9380 view.selections.display_ranges(cx),
9381 vec![
9382 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9383 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9384 ]
9385 );
9386 });
9387
9388 view.update(cx, |view, cx| {
9389 view.add_selection_below(&AddSelectionBelow, cx);
9390 assert_eq!(
9391 view.selections.display_ranges(cx),
9392 vec![
9393 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9394 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9395 ]
9396 );
9397 });
9398
9399 view.update(cx, |view, cx| {
9400 view.add_selection_above(&AddSelectionAbove, cx);
9401 assert_eq!(
9402 view.selections.display_ranges(cx),
9403 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9404 );
9405 });
9406
9407 view.update(cx, |view, cx| {
9408 view.add_selection_above(&AddSelectionAbove, cx);
9409 assert_eq!(
9410 view.selections.display_ranges(cx),
9411 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9412 );
9413 });
9414
9415 view.update(cx, |view, cx| {
9416 view.change_selections(None, cx, |s| {
9417 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9418 });
9419 view.add_selection_below(&AddSelectionBelow, cx);
9420 assert_eq!(
9421 view.selections.display_ranges(cx),
9422 vec![
9423 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9424 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9425 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9426 ]
9427 );
9428 });
9429
9430 view.update(cx, |view, cx| {
9431 view.add_selection_below(&AddSelectionBelow, cx);
9432 assert_eq!(
9433 view.selections.display_ranges(cx),
9434 vec![
9435 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9436 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9437 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9438 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9439 ]
9440 );
9441 });
9442
9443 view.update(cx, |view, cx| {
9444 view.add_selection_above(&AddSelectionAbove, cx);
9445 assert_eq!(
9446 view.selections.display_ranges(cx),
9447 vec![
9448 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9449 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9450 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9451 ]
9452 );
9453 });
9454
9455 view.update(cx, |view, cx| {
9456 view.change_selections(None, cx, |s| {
9457 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9458 });
9459 });
9460 view.update(cx, |view, cx| {
9461 view.add_selection_above(&AddSelectionAbove, cx);
9462 assert_eq!(
9463 view.selections.display_ranges(cx),
9464 vec![
9465 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9466 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9467 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9468 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9469 ]
9470 );
9471 });
9472
9473 view.update(cx, |view, cx| {
9474 view.add_selection_below(&AddSelectionBelow, cx);
9475 assert_eq!(
9476 view.selections.display_ranges(cx),
9477 vec![
9478 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9479 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9480 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9481 ]
9482 );
9483 });
9484 }
9485
9486 #[gpui::test]
9487 async fn test_select_next(cx: &mut gpui::TestAppContext) {
9488 let mut cx = EditorTestContext::new(cx);
9489 cx.set_state("abc\nˇabc abc\ndefabc\nabc");
9490
9491 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9492 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9493
9494 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9495 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9496
9497 cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
9498 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9499
9500 cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
9501 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9502
9503 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9504 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9505
9506 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9507 cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9508 }
9509
9510 #[gpui::test]
9511 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9512 cx.update(|cx| cx.set_global(Settings::test(cx)));
9513 let language = Arc::new(Language::new(
9514 LanguageConfig::default(),
9515 Some(tree_sitter_rust::language()),
9516 ));
9517
9518 let text = r#"
9519 use mod1::mod2::{mod3, mod4};
9520
9521 fn fn_1(param1: bool, param2: &str) {
9522 let var1 = "text";
9523 }
9524 "#
9525 .unindent();
9526
9527 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9528 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9529 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9530 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9531 .await;
9532
9533 view.update(cx, |view, cx| {
9534 view.change_selections(None, cx, |s| {
9535 s.select_display_ranges([
9536 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9537 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9538 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9539 ]);
9540 });
9541 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9542 });
9543 assert_eq!(
9544 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9545 &[
9546 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9547 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9548 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9549 ]
9550 );
9551
9552 view.update(cx, |view, cx| {
9553 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9554 });
9555 assert_eq!(
9556 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9557 &[
9558 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9559 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9560 ]
9561 );
9562
9563 view.update(cx, |view, cx| {
9564 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9565 });
9566 assert_eq!(
9567 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9568 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9569 );
9570
9571 // Trying to expand the selected syntax node one more time has no effect.
9572 view.update(cx, |view, cx| {
9573 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9574 });
9575 assert_eq!(
9576 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9577 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9578 );
9579
9580 view.update(cx, |view, cx| {
9581 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9582 });
9583 assert_eq!(
9584 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9585 &[
9586 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9587 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9588 ]
9589 );
9590
9591 view.update(cx, |view, cx| {
9592 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9593 });
9594 assert_eq!(
9595 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9596 &[
9597 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9598 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9599 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9600 ]
9601 );
9602
9603 view.update(cx, |view, cx| {
9604 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9605 });
9606 assert_eq!(
9607 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9608 &[
9609 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9610 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9611 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9612 ]
9613 );
9614
9615 // Trying to shrink the selected syntax node one more time has no effect.
9616 view.update(cx, |view, cx| {
9617 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9618 });
9619 assert_eq!(
9620 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9621 &[
9622 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9623 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9624 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9625 ]
9626 );
9627
9628 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9629 // a fold.
9630 view.update(cx, |view, cx| {
9631 view.fold_ranges(
9632 vec![
9633 Point::new(0, 21)..Point::new(0, 24),
9634 Point::new(3, 20)..Point::new(3, 22),
9635 ],
9636 cx,
9637 );
9638 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9639 });
9640 assert_eq!(
9641 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9642 &[
9643 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9644 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9645 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9646 ]
9647 );
9648 }
9649
9650 #[gpui::test]
9651 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9652 cx.update(|cx| cx.set_global(Settings::test(cx)));
9653 let language = Arc::new(
9654 Language::new(
9655 LanguageConfig {
9656 brackets: vec![
9657 BracketPair {
9658 start: "{".to_string(),
9659 end: "}".to_string(),
9660 close: false,
9661 newline: true,
9662 },
9663 BracketPair {
9664 start: "(".to_string(),
9665 end: ")".to_string(),
9666 close: false,
9667 newline: true,
9668 },
9669 ],
9670 ..Default::default()
9671 },
9672 Some(tree_sitter_rust::language()),
9673 )
9674 .with_indents_query(
9675 r#"
9676 (_ "(" ")" @end) @indent
9677 (_ "{" "}" @end) @indent
9678 "#,
9679 )
9680 .unwrap(),
9681 );
9682
9683 let text = "fn a() {}";
9684
9685 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9686 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9687 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9688 editor
9689 .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9690 .await;
9691
9692 editor.update(cx, |editor, cx| {
9693 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9694 editor.newline(&Newline, cx);
9695 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9696 assert_eq!(
9697 editor.selections.ranges(cx),
9698 &[
9699 Point::new(1, 4)..Point::new(1, 4),
9700 Point::new(3, 4)..Point::new(3, 4),
9701 Point::new(5, 0)..Point::new(5, 0)
9702 ]
9703 );
9704 });
9705 }
9706
9707 #[gpui::test]
9708 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9709 let mut cx = EditorTestContext::new(cx);
9710
9711 let language = Arc::new(Language::new(
9712 LanguageConfig {
9713 brackets: vec![
9714 BracketPair {
9715 start: "{".to_string(),
9716 end: "}".to_string(),
9717 close: true,
9718 newline: true,
9719 },
9720 BracketPair {
9721 start: "/*".to_string(),
9722 end: " */".to_string(),
9723 close: true,
9724 newline: true,
9725 },
9726 BracketPair {
9727 start: "[".to_string(),
9728 end: "]".to_string(),
9729 close: false,
9730 newline: true,
9731 },
9732 ],
9733 autoclose_before: "})]".to_string(),
9734 ..Default::default()
9735 },
9736 Some(tree_sitter_rust::language()),
9737 ));
9738
9739 let registry = Arc::new(LanguageRegistry::test());
9740 registry.add(language.clone());
9741 cx.update_buffer(|buffer, cx| {
9742 buffer.set_language_registry(registry);
9743 buffer.set_language(Some(language), cx);
9744 });
9745
9746 cx.set_state(
9747 &r#"
9748 🏀ˇ
9749 εˇ
9750 ❤️ˇ
9751 "#
9752 .unindent(),
9753 );
9754
9755 // autoclose multiple nested brackets at multiple cursors
9756 cx.update_editor(|view, cx| {
9757 view.handle_input("{", cx);
9758 view.handle_input("{", cx);
9759 view.handle_input("{", cx);
9760 });
9761 cx.assert_editor_state(
9762 &"
9763 🏀{{{ˇ}}}
9764 ε{{{ˇ}}}
9765 ❤️{{{ˇ}}}
9766 "
9767 .unindent(),
9768 );
9769
9770 // skip over the auto-closed brackets when typing a closing bracket
9771 cx.update_editor(|view, cx| {
9772 view.move_right(&MoveRight, cx);
9773 view.handle_input("}", cx);
9774 view.handle_input("}", cx);
9775 view.handle_input("}", cx);
9776 });
9777 cx.assert_editor_state(
9778 &"
9779 🏀{{{}}}}ˇ
9780 ε{{{}}}}ˇ
9781 ❤️{{{}}}}ˇ
9782 "
9783 .unindent(),
9784 );
9785
9786 // autoclose multi-character pairs
9787 cx.set_state(
9788 &"
9789 ˇ
9790 ˇ
9791 "
9792 .unindent(),
9793 );
9794 cx.update_editor(|view, cx| {
9795 view.handle_input("/", cx);
9796 view.handle_input("*", cx);
9797 });
9798 cx.assert_editor_state(
9799 &"
9800 /*ˇ */
9801 /*ˇ */
9802 "
9803 .unindent(),
9804 );
9805
9806 // one cursor autocloses a multi-character pair, one cursor
9807 // does not autoclose.
9808 cx.set_state(
9809 &"
9810 /ˇ
9811 ˇ
9812 "
9813 .unindent(),
9814 );
9815 cx.update_editor(|view, cx| view.handle_input("*", cx));
9816 cx.assert_editor_state(
9817 &"
9818 /*ˇ */
9819 *ˇ
9820 "
9821 .unindent(),
9822 );
9823
9824 // Don't autoclose if the next character isn't whitespace and isn't
9825 // listed in the language's "autoclose_before" section.
9826 cx.set_state("ˇa b");
9827 cx.update_editor(|view, cx| view.handle_input("{", cx));
9828 cx.assert_editor_state("{ˇa b");
9829
9830 // Surround with brackets if text is selected
9831 cx.set_state("«aˇ» b");
9832 cx.update_editor(|view, cx| view.handle_input("{", cx));
9833 cx.assert_editor_state("{«aˇ»} b");
9834 }
9835
9836 #[gpui::test]
9837 async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
9838 let mut cx = EditorTestContext::new(cx);
9839
9840 let html_language = Arc::new(
9841 Language::new(
9842 LanguageConfig {
9843 name: "HTML".into(),
9844 brackets: vec![
9845 BracketPair {
9846 start: "<".into(),
9847 end: ">".into(),
9848 ..Default::default()
9849 },
9850 BracketPair {
9851 start: "{".into(),
9852 end: "}".into(),
9853 ..Default::default()
9854 },
9855 BracketPair {
9856 start: "(".into(),
9857 end: ")".into(),
9858 ..Default::default()
9859 },
9860 ],
9861 autoclose_before: "})]>".into(),
9862 ..Default::default()
9863 },
9864 Some(tree_sitter_html::language()),
9865 )
9866 .with_injection_query(
9867 r#"
9868 (script_element
9869 (raw_text) @content
9870 (#set! "language" "javascript"))
9871 "#,
9872 )
9873 .unwrap(),
9874 );
9875
9876 let javascript_language = Arc::new(Language::new(
9877 LanguageConfig {
9878 name: "JavaScript".into(),
9879 brackets: vec![
9880 BracketPair {
9881 start: "/*".into(),
9882 end: " */".into(),
9883 ..Default::default()
9884 },
9885 BracketPair {
9886 start: "{".into(),
9887 end: "}".into(),
9888 ..Default::default()
9889 },
9890 BracketPair {
9891 start: "(".into(),
9892 end: ")".into(),
9893 ..Default::default()
9894 },
9895 ],
9896 autoclose_before: "})]>".into(),
9897 ..Default::default()
9898 },
9899 Some(tree_sitter_javascript::language()),
9900 ));
9901
9902 let registry = Arc::new(LanguageRegistry::test());
9903 registry.add(html_language.clone());
9904 registry.add(javascript_language.clone());
9905
9906 cx.update_buffer(|buffer, cx| {
9907 buffer.set_language_registry(registry);
9908 buffer.set_language(Some(html_language), cx);
9909 });
9910
9911 cx.set_state(
9912 &r#"
9913 <body>ˇ
9914 <script>
9915 var x = 1;ˇ
9916 </script>
9917 </body>ˇ
9918 "#
9919 .unindent(),
9920 );
9921
9922 // Precondition: different languages are active at different locations.
9923 cx.update_editor(|editor, cx| {
9924 let snapshot = editor.snapshot(cx);
9925 let cursors = editor.selections.ranges::<usize>(cx);
9926 let languages = cursors
9927 .iter()
9928 .map(|c| snapshot.language_at(c.start).unwrap().name())
9929 .collect::<Vec<_>>();
9930 assert_eq!(
9931 languages,
9932 &["HTML".into(), "JavaScript".into(), "HTML".into()]
9933 );
9934 });
9935
9936 // Angle brackets autoclose in HTML, but not JavaScript.
9937 cx.update_editor(|editor, cx| {
9938 editor.handle_input("<", cx);
9939 editor.handle_input("a", cx);
9940 });
9941 cx.assert_editor_state(
9942 &r#"
9943 <body><aˇ>
9944 <script>
9945 var x = 1;<aˇ
9946 </script>
9947 </body><aˇ>
9948 "#
9949 .unindent(),
9950 );
9951
9952 // Curly braces and parens autoclose in both HTML and JavaScript.
9953 cx.update_editor(|editor, cx| {
9954 editor.handle_input(" b=", cx);
9955 editor.handle_input("{", cx);
9956 editor.handle_input("c", cx);
9957 editor.handle_input("(", cx);
9958 });
9959 cx.assert_editor_state(
9960 &r#"
9961 <body><a b={c(ˇ)}>
9962 <script>
9963 var x = 1;<a b={c(ˇ)}
9964 </script>
9965 </body><a b={c(ˇ)}>
9966 "#
9967 .unindent(),
9968 );
9969
9970 // Brackets that were already autoclosed are skipped.
9971 cx.update_editor(|editor, cx| {
9972 editor.handle_input(")", cx);
9973 editor.handle_input("d", cx);
9974 editor.handle_input("}", cx);
9975 });
9976 cx.assert_editor_state(
9977 &r#"
9978 <body><a b={c()d}ˇ>
9979 <script>
9980 var x = 1;<a b={c()d}ˇ
9981 </script>
9982 </body><a b={c()d}ˇ>
9983 "#
9984 .unindent(),
9985 );
9986 cx.update_editor(|editor, cx| {
9987 editor.handle_input(">", cx);
9988 });
9989 cx.assert_editor_state(
9990 &r#"
9991 <body><a b={c()d}>ˇ
9992 <script>
9993 var x = 1;<a b={c()d}>ˇ
9994 </script>
9995 </body><a b={c()d}>ˇ
9996 "#
9997 .unindent(),
9998 );
9999
10000 // Reset
10001 cx.set_state(
10002 &r#"
10003 <body>ˇ
10004 <script>
10005 var x = 1;ˇ
10006 </script>
10007 </body>ˇ
10008 "#
10009 .unindent(),
10010 );
10011
10012 cx.update_editor(|editor, cx| {
10013 editor.handle_input("<", cx);
10014 });
10015 cx.assert_editor_state(
10016 &r#"
10017 <body><ˇ>
10018 <script>
10019 var x = 1;<ˇ
10020 </script>
10021 </body><ˇ>
10022 "#
10023 .unindent(),
10024 );
10025
10026 // When backspacing, the closing angle brackets are removed.
10027 cx.update_editor(|editor, cx| {
10028 editor.backspace(&Backspace, cx);
10029 });
10030 cx.assert_editor_state(
10031 &r#"
10032 <body>ˇ
10033 <script>
10034 var x = 1;ˇ
10035 </script>
10036 </body>ˇ
10037 "#
10038 .unindent(),
10039 );
10040
10041 // Block comments autoclose in JavaScript, but not HTML.
10042 cx.update_editor(|editor, cx| {
10043 editor.handle_input("/", cx);
10044 editor.handle_input("*", cx);
10045 });
10046 cx.assert_editor_state(
10047 &r#"
10048 <body>/*ˇ
10049 <script>
10050 var x = 1;/*ˇ */
10051 </script>
10052 </body>/*ˇ
10053 "#
10054 .unindent(),
10055 );
10056 }
10057
10058 #[gpui::test]
10059 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
10060 cx.update(|cx| cx.set_global(Settings::test(cx)));
10061 let language = Arc::new(Language::new(
10062 LanguageConfig {
10063 brackets: vec![BracketPair {
10064 start: "{".to_string(),
10065 end: "}".to_string(),
10066 close: true,
10067 newline: true,
10068 }],
10069 ..Default::default()
10070 },
10071 Some(tree_sitter_rust::language()),
10072 ));
10073
10074 let text = r#"
10075 a
10076 b
10077 c
10078 "#
10079 .unindent();
10080
10081 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10082 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10083 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10084 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10085 .await;
10086
10087 view.update(cx, |view, cx| {
10088 view.change_selections(None, cx, |s| {
10089 s.select_display_ranges([
10090 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
10091 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
10092 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
10093 ])
10094 });
10095
10096 view.handle_input("{", cx);
10097 view.handle_input("{", cx);
10098 view.handle_input("{", cx);
10099 assert_eq!(
10100 view.text(cx),
10101 "
10102 {{{a}}}
10103 {{{b}}}
10104 {{{c}}}
10105 "
10106 .unindent()
10107 );
10108 assert_eq!(
10109 view.selections.display_ranges(cx),
10110 [
10111 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
10112 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
10113 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
10114 ]
10115 );
10116
10117 view.undo(&Undo, cx);
10118 assert_eq!(
10119 view.text(cx),
10120 "
10121 a
10122 b
10123 c
10124 "
10125 .unindent()
10126 );
10127 assert_eq!(
10128 view.selections.display_ranges(cx),
10129 [
10130 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
10131 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
10132 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
10133 ]
10134 );
10135 });
10136 }
10137
10138 #[gpui::test]
10139 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
10140 cx.update(|cx| cx.set_global(Settings::test(cx)));
10141 let language = Arc::new(Language::new(
10142 LanguageConfig {
10143 brackets: vec![BracketPair {
10144 start: "{".to_string(),
10145 end: "}".to_string(),
10146 close: true,
10147 newline: true,
10148 }],
10149 autoclose_before: "}".to_string(),
10150 ..Default::default()
10151 },
10152 Some(tree_sitter_rust::language()),
10153 ));
10154
10155 let text = r#"
10156 a
10157 b
10158 c
10159 "#
10160 .unindent();
10161
10162 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10163 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10164 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10165 editor
10166 .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10167 .await;
10168
10169 editor.update(cx, |editor, cx| {
10170 editor.change_selections(None, cx, |s| {
10171 s.select_ranges([
10172 Point::new(0, 1)..Point::new(0, 1),
10173 Point::new(1, 1)..Point::new(1, 1),
10174 Point::new(2, 1)..Point::new(2, 1),
10175 ])
10176 });
10177
10178 editor.handle_input("{", cx);
10179 editor.handle_input("{", cx);
10180 editor.handle_input("_", cx);
10181 assert_eq!(
10182 editor.text(cx),
10183 "
10184 a{{_}}
10185 b{{_}}
10186 c{{_}}
10187 "
10188 .unindent()
10189 );
10190 assert_eq!(
10191 editor.selections.ranges::<Point>(cx),
10192 [
10193 Point::new(0, 4)..Point::new(0, 4),
10194 Point::new(1, 4)..Point::new(1, 4),
10195 Point::new(2, 4)..Point::new(2, 4)
10196 ]
10197 );
10198
10199 editor.backspace(&Default::default(), cx);
10200 editor.backspace(&Default::default(), cx);
10201 assert_eq!(
10202 editor.text(cx),
10203 "
10204 a{}
10205 b{}
10206 c{}
10207 "
10208 .unindent()
10209 );
10210 assert_eq!(
10211 editor.selections.ranges::<Point>(cx),
10212 [
10213 Point::new(0, 2)..Point::new(0, 2),
10214 Point::new(1, 2)..Point::new(1, 2),
10215 Point::new(2, 2)..Point::new(2, 2)
10216 ]
10217 );
10218
10219 editor.delete_to_previous_word_start(&Default::default(), cx);
10220 assert_eq!(
10221 editor.text(cx),
10222 "
10223 a
10224 b
10225 c
10226 "
10227 .unindent()
10228 );
10229 assert_eq!(
10230 editor.selections.ranges::<Point>(cx),
10231 [
10232 Point::new(0, 1)..Point::new(0, 1),
10233 Point::new(1, 1)..Point::new(1, 1),
10234 Point::new(2, 1)..Point::new(2, 1)
10235 ]
10236 );
10237 });
10238 }
10239
10240 #[gpui::test]
10241 async fn test_snippets(cx: &mut gpui::TestAppContext) {
10242 cx.update(|cx| cx.set_global(Settings::test(cx)));
10243
10244 let (text, insertion_ranges) = marked_text_ranges(
10245 indoc! {"
10246 a.ˇ b
10247 a.ˇ b
10248 a.ˇ b
10249 "},
10250 false,
10251 );
10252
10253 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
10254 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10255
10256 editor.update(cx, |editor, cx| {
10257 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
10258
10259 editor
10260 .insert_snippet(&insertion_ranges, snippet, cx)
10261 .unwrap();
10262
10263 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
10264 let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
10265 assert_eq!(editor.text(cx), expected_text);
10266 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
10267 }
10268
10269 assert(
10270 editor,
10271 cx,
10272 indoc! {"
10273 a.f(«one», two, «three») b
10274 a.f(«one», two, «three») b
10275 a.f(«one», two, «three») b
10276 "},
10277 );
10278
10279 // Can't move earlier than the first tab stop
10280 assert!(!editor.move_to_prev_snippet_tabstop(cx));
10281 assert(
10282 editor,
10283 cx,
10284 indoc! {"
10285 a.f(«one», two, «three») b
10286 a.f(«one», two, «three») b
10287 a.f(«one», two, «three») b
10288 "},
10289 );
10290
10291 assert!(editor.move_to_next_snippet_tabstop(cx));
10292 assert(
10293 editor,
10294 cx,
10295 indoc! {"
10296 a.f(one, «two», three) b
10297 a.f(one, «two», three) b
10298 a.f(one, «two», three) b
10299 "},
10300 );
10301
10302 editor.move_to_prev_snippet_tabstop(cx);
10303 assert(
10304 editor,
10305 cx,
10306 indoc! {"
10307 a.f(«one», two, «three») b
10308 a.f(«one», two, «three») b
10309 a.f(«one», two, «three») b
10310 "},
10311 );
10312
10313 assert!(editor.move_to_next_snippet_tabstop(cx));
10314 assert(
10315 editor,
10316 cx,
10317 indoc! {"
10318 a.f(one, «two», three) b
10319 a.f(one, «two», three) b
10320 a.f(one, «two», three) b
10321 "},
10322 );
10323 assert!(editor.move_to_next_snippet_tabstop(cx));
10324 assert(
10325 editor,
10326 cx,
10327 indoc! {"
10328 a.f(one, two, three)ˇ b
10329 a.f(one, two, three)ˇ b
10330 a.f(one, two, three)ˇ b
10331 "},
10332 );
10333
10334 // As soon as the last tab stop is reached, snippet state is gone
10335 editor.move_to_prev_snippet_tabstop(cx);
10336 assert(
10337 editor,
10338 cx,
10339 indoc! {"
10340 a.f(one, two, three)ˇ b
10341 a.f(one, two, three)ˇ b
10342 a.f(one, two, three)ˇ b
10343 "},
10344 );
10345 });
10346 }
10347
10348 #[gpui::test]
10349 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
10350 cx.foreground().forbid_parking();
10351
10352 let mut language = Language::new(
10353 LanguageConfig {
10354 name: "Rust".into(),
10355 path_suffixes: vec!["rs".to_string()],
10356 ..Default::default()
10357 },
10358 Some(tree_sitter_rust::language()),
10359 );
10360 let mut fake_servers = language
10361 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10362 capabilities: lsp::ServerCapabilities {
10363 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10364 ..Default::default()
10365 },
10366 ..Default::default()
10367 }))
10368 .await;
10369
10370 let fs = FakeFs::new(cx.background());
10371 fs.insert_file("/file.rs", Default::default()).await;
10372
10373 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10374 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10375 let buffer = project
10376 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10377 .await
10378 .unwrap();
10379
10380 cx.foreground().start_waiting();
10381 let fake_server = fake_servers.next().await.unwrap();
10382
10383 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10384 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10385 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10386 assert!(cx.read(|cx| editor.is_dirty(cx)));
10387
10388 let save = cx.update(|cx| editor.save(project.clone(), cx));
10389 fake_server
10390 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10391 assert_eq!(
10392 params.text_document.uri,
10393 lsp::Url::from_file_path("/file.rs").unwrap()
10394 );
10395 assert_eq!(params.options.tab_size, 4);
10396 Ok(Some(vec![lsp::TextEdit::new(
10397 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10398 ", ".to_string(),
10399 )]))
10400 })
10401 .next()
10402 .await;
10403 cx.foreground().start_waiting();
10404 save.await.unwrap();
10405 assert_eq!(
10406 editor.read_with(cx, |editor, cx| editor.text(cx)),
10407 "one, two\nthree\n"
10408 );
10409 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10410
10411 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10412 assert!(cx.read(|cx| editor.is_dirty(cx)));
10413
10414 // Ensure we can still save even if formatting hangs.
10415 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10416 assert_eq!(
10417 params.text_document.uri,
10418 lsp::Url::from_file_path("/file.rs").unwrap()
10419 );
10420 futures::future::pending::<()>().await;
10421 unreachable!()
10422 });
10423 let save = cx.update(|cx| editor.save(project.clone(), cx));
10424 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10425 cx.foreground().start_waiting();
10426 save.await.unwrap();
10427 assert_eq!(
10428 editor.read_with(cx, |editor, cx| editor.text(cx)),
10429 "one\ntwo\nthree\n"
10430 );
10431 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10432
10433 // Set rust language override and assert overriden tabsize is sent to language server
10434 cx.update(|cx| {
10435 cx.update_global::<Settings, _, _>(|settings, _| {
10436 settings.language_overrides.insert(
10437 "Rust".into(),
10438 EditorSettings {
10439 tab_size: Some(8.try_into().unwrap()),
10440 ..Default::default()
10441 },
10442 );
10443 })
10444 });
10445
10446 let save = cx.update(|cx| editor.save(project.clone(), cx));
10447 fake_server
10448 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10449 assert_eq!(
10450 params.text_document.uri,
10451 lsp::Url::from_file_path("/file.rs").unwrap()
10452 );
10453 assert_eq!(params.options.tab_size, 8);
10454 Ok(Some(vec![]))
10455 })
10456 .next()
10457 .await;
10458 cx.foreground().start_waiting();
10459 save.await.unwrap();
10460 }
10461
10462 #[gpui::test]
10463 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10464 cx.foreground().forbid_parking();
10465
10466 let mut language = Language::new(
10467 LanguageConfig {
10468 name: "Rust".into(),
10469 path_suffixes: vec!["rs".to_string()],
10470 ..Default::default()
10471 },
10472 Some(tree_sitter_rust::language()),
10473 );
10474 let mut fake_servers = language
10475 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10476 capabilities: lsp::ServerCapabilities {
10477 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10478 ..Default::default()
10479 },
10480 ..Default::default()
10481 }))
10482 .await;
10483
10484 let fs = FakeFs::new(cx.background());
10485 fs.insert_file("/file.rs", Default::default()).await;
10486
10487 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10488 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10489 let buffer = project
10490 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10491 .await
10492 .unwrap();
10493
10494 cx.foreground().start_waiting();
10495 let fake_server = fake_servers.next().await.unwrap();
10496
10497 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10498 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10499 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10500 assert!(cx.read(|cx| editor.is_dirty(cx)));
10501
10502 let save = cx.update(|cx| editor.save(project.clone(), cx));
10503 fake_server
10504 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10505 assert_eq!(
10506 params.text_document.uri,
10507 lsp::Url::from_file_path("/file.rs").unwrap()
10508 );
10509 assert_eq!(params.options.tab_size, 4);
10510 Ok(Some(vec![lsp::TextEdit::new(
10511 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10512 ", ".to_string(),
10513 )]))
10514 })
10515 .next()
10516 .await;
10517 cx.foreground().start_waiting();
10518 save.await.unwrap();
10519 assert_eq!(
10520 editor.read_with(cx, |editor, cx| editor.text(cx)),
10521 "one, two\nthree\n"
10522 );
10523 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10524
10525 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10526 assert!(cx.read(|cx| editor.is_dirty(cx)));
10527
10528 // Ensure we can still save even if formatting hangs.
10529 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10530 move |params, _| async move {
10531 assert_eq!(
10532 params.text_document.uri,
10533 lsp::Url::from_file_path("/file.rs").unwrap()
10534 );
10535 futures::future::pending::<()>().await;
10536 unreachable!()
10537 },
10538 );
10539 let save = cx.update(|cx| editor.save(project.clone(), cx));
10540 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10541 cx.foreground().start_waiting();
10542 save.await.unwrap();
10543 assert_eq!(
10544 editor.read_with(cx, |editor, cx| editor.text(cx)),
10545 "one\ntwo\nthree\n"
10546 );
10547 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10548
10549 // Set rust language override and assert overriden tabsize is sent to language server
10550 cx.update(|cx| {
10551 cx.update_global::<Settings, _, _>(|settings, _| {
10552 settings.language_overrides.insert(
10553 "Rust".into(),
10554 EditorSettings {
10555 tab_size: Some(8.try_into().unwrap()),
10556 ..Default::default()
10557 },
10558 );
10559 })
10560 });
10561
10562 let save = cx.update(|cx| editor.save(project.clone(), cx));
10563 fake_server
10564 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10565 assert_eq!(
10566 params.text_document.uri,
10567 lsp::Url::from_file_path("/file.rs").unwrap()
10568 );
10569 assert_eq!(params.options.tab_size, 8);
10570 Ok(Some(vec![]))
10571 })
10572 .next()
10573 .await;
10574 cx.foreground().start_waiting();
10575 save.await.unwrap();
10576 }
10577
10578 #[gpui::test]
10579 async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
10580 cx.foreground().forbid_parking();
10581
10582 let mut language = Language::new(
10583 LanguageConfig {
10584 name: "Rust".into(),
10585 path_suffixes: vec!["rs".to_string()],
10586 ..Default::default()
10587 },
10588 Some(tree_sitter_rust::language()),
10589 );
10590 let mut fake_servers = language
10591 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10592 capabilities: lsp::ServerCapabilities {
10593 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10594 ..Default::default()
10595 },
10596 ..Default::default()
10597 }))
10598 .await;
10599
10600 let fs = FakeFs::new(cx.background());
10601 fs.insert_file("/file.rs", Default::default()).await;
10602
10603 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10604 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10605 let buffer = project
10606 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10607 .await
10608 .unwrap();
10609
10610 cx.foreground().start_waiting();
10611 let fake_server = fake_servers.next().await.unwrap();
10612
10613 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10614 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10615 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10616
10617 let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
10618 fake_server
10619 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10620 assert_eq!(
10621 params.text_document.uri,
10622 lsp::Url::from_file_path("/file.rs").unwrap()
10623 );
10624 assert_eq!(params.options.tab_size, 4);
10625 Ok(Some(vec![lsp::TextEdit::new(
10626 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10627 ", ".to_string(),
10628 )]))
10629 })
10630 .next()
10631 .await;
10632 cx.foreground().start_waiting();
10633 format.await.unwrap();
10634 assert_eq!(
10635 editor.read_with(cx, |editor, cx| editor.text(cx)),
10636 "one, two\nthree\n"
10637 );
10638
10639 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10640 // Ensure we don't lock if formatting hangs.
10641 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10642 assert_eq!(
10643 params.text_document.uri,
10644 lsp::Url::from_file_path("/file.rs").unwrap()
10645 );
10646 futures::future::pending::<()>().await;
10647 unreachable!()
10648 });
10649 let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
10650 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10651 cx.foreground().start_waiting();
10652 format.await.unwrap();
10653 assert_eq!(
10654 editor.read_with(cx, |editor, cx| editor.text(cx)),
10655 "one\ntwo\nthree\n"
10656 );
10657 }
10658
10659 #[gpui::test]
10660 async fn test_completion(cx: &mut gpui::TestAppContext) {
10661 let mut cx = EditorLspTestContext::new_rust(
10662 lsp::ServerCapabilities {
10663 completion_provider: Some(lsp::CompletionOptions {
10664 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10665 ..Default::default()
10666 }),
10667 ..Default::default()
10668 },
10669 cx,
10670 )
10671 .await;
10672
10673 cx.set_state(indoc! {"
10674 oneˇ
10675 two
10676 three
10677 "});
10678 cx.simulate_keystroke(".");
10679 handle_completion_request(
10680 &mut cx,
10681 indoc! {"
10682 one.|<>
10683 two
10684 three
10685 "},
10686 vec!["first_completion", "second_completion"],
10687 )
10688 .await;
10689 cx.condition(|editor, _| editor.context_menu_visible())
10690 .await;
10691 let apply_additional_edits = cx.update_editor(|editor, cx| {
10692 editor.move_down(&MoveDown, cx);
10693 editor
10694 .confirm_completion(&ConfirmCompletion::default(), cx)
10695 .unwrap()
10696 });
10697 cx.assert_editor_state(indoc! {"
10698 one.second_completionˇ
10699 two
10700 three
10701 "});
10702
10703 handle_resolve_completion_request(
10704 &mut cx,
10705 Some((
10706 indoc! {"
10707 one.second_completion
10708 two
10709 threeˇ
10710 "},
10711 "\nadditional edit",
10712 )),
10713 )
10714 .await;
10715 apply_additional_edits.await.unwrap();
10716 cx.assert_editor_state(indoc! {"
10717 one.second_completionˇ
10718 two
10719 three
10720 additional edit
10721 "});
10722
10723 cx.set_state(indoc! {"
10724 one.second_completion
10725 twoˇ
10726 threeˇ
10727 additional edit
10728 "});
10729 cx.simulate_keystroke(" ");
10730 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10731 cx.simulate_keystroke("s");
10732 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10733
10734 cx.assert_editor_state(indoc! {"
10735 one.second_completion
10736 two sˇ
10737 three sˇ
10738 additional edit
10739 "});
10740 //
10741 handle_completion_request(
10742 &mut cx,
10743 indoc! {"
10744 one.second_completion
10745 two s
10746 three <s|>
10747 additional edit
10748 "},
10749 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10750 )
10751 .await;
10752 cx.condition(|editor, _| editor.context_menu_visible())
10753 .await;
10754
10755 cx.simulate_keystroke("i");
10756
10757 handle_completion_request(
10758 &mut cx,
10759 indoc! {"
10760 one.second_completion
10761 two si
10762 three <si|>
10763 additional edit
10764 "},
10765 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10766 )
10767 .await;
10768 cx.condition(|editor, _| editor.context_menu_visible())
10769 .await;
10770
10771 let apply_additional_edits = cx.update_editor(|editor, cx| {
10772 editor
10773 .confirm_completion(&ConfirmCompletion::default(), cx)
10774 .unwrap()
10775 });
10776 cx.assert_editor_state(indoc! {"
10777 one.second_completion
10778 two sixth_completionˇ
10779 three sixth_completionˇ
10780 additional edit
10781 "});
10782
10783 handle_resolve_completion_request(&mut cx, None).await;
10784 apply_additional_edits.await.unwrap();
10785
10786 cx.update(|cx| {
10787 cx.update_global::<Settings, _, _>(|settings, _| {
10788 settings.show_completions_on_input = false;
10789 })
10790 });
10791 cx.set_state("editorˇ");
10792 cx.simulate_keystroke(".");
10793 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10794 cx.simulate_keystroke("c");
10795 cx.simulate_keystroke("l");
10796 cx.simulate_keystroke("o");
10797 cx.assert_editor_state("editor.cloˇ");
10798 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10799 cx.update_editor(|editor, cx| {
10800 editor.show_completions(&ShowCompletions, cx);
10801 });
10802 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10803 cx.condition(|editor, _| editor.context_menu_visible())
10804 .await;
10805 let apply_additional_edits = cx.update_editor(|editor, cx| {
10806 editor
10807 .confirm_completion(&ConfirmCompletion::default(), cx)
10808 .unwrap()
10809 });
10810 cx.assert_editor_state("editor.closeˇ");
10811 handle_resolve_completion_request(&mut cx, None).await;
10812 apply_additional_edits.await.unwrap();
10813
10814 // Handle completion request passing a marked string specifying where the completion
10815 // should be triggered from using '|' character, what range should be replaced, and what completions
10816 // should be returned using '<' and '>' to delimit the range
10817 async fn handle_completion_request<'a>(
10818 cx: &mut EditorLspTestContext<'a>,
10819 marked_string: &str,
10820 completions: Vec<&'static str>,
10821 ) {
10822 let complete_from_marker: TextRangeMarker = '|'.into();
10823 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10824 let (_, mut marked_ranges) = marked_text_ranges_by(
10825 marked_string,
10826 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10827 );
10828
10829 let complete_from_position =
10830 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10831 let replace_range =
10832 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10833
10834 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10835 let completions = completions.clone();
10836 async move {
10837 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10838 assert_eq!(
10839 params.text_document_position.position,
10840 complete_from_position
10841 );
10842 Ok(Some(lsp::CompletionResponse::Array(
10843 completions
10844 .iter()
10845 .map(|completion_text| lsp::CompletionItem {
10846 label: completion_text.to_string(),
10847 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10848 range: replace_range,
10849 new_text: completion_text.to_string(),
10850 })),
10851 ..Default::default()
10852 })
10853 .collect(),
10854 )))
10855 }
10856 })
10857 .next()
10858 .await;
10859 }
10860
10861 async fn handle_resolve_completion_request<'a>(
10862 cx: &mut EditorLspTestContext<'a>,
10863 edit: Option<(&'static str, &'static str)>,
10864 ) {
10865 let edit = edit.map(|(marked_string, new_text)| {
10866 let (_, marked_ranges) = marked_text_ranges(marked_string, false);
10867 let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
10868 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10869 });
10870
10871 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10872 let edit = edit.clone();
10873 async move {
10874 Ok(lsp::CompletionItem {
10875 additional_text_edits: edit,
10876 ..Default::default()
10877 })
10878 }
10879 })
10880 .next()
10881 .await;
10882 }
10883 }
10884
10885 #[gpui::test]
10886 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10887 cx.update(|cx| cx.set_global(Settings::test(cx)));
10888 let language = Arc::new(Language::new(
10889 LanguageConfig {
10890 line_comment: Some("// ".into()),
10891 ..Default::default()
10892 },
10893 Some(tree_sitter_rust::language()),
10894 ));
10895
10896 let text = "
10897 fn a() {
10898 //b();
10899 // c();
10900 // d();
10901 }
10902 "
10903 .unindent();
10904
10905 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10906 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10907 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10908
10909 view.update(cx, |editor, cx| {
10910 // If multiple selections intersect a line, the line is only
10911 // toggled once.
10912 editor.change_selections(None, cx, |s| {
10913 s.select_display_ranges([
10914 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10915 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10916 ])
10917 });
10918 editor.toggle_comments(&ToggleComments, cx);
10919 assert_eq!(
10920 editor.text(cx),
10921 "
10922 fn a() {
10923 b();
10924 c();
10925 d();
10926 }
10927 "
10928 .unindent()
10929 );
10930
10931 // The comment prefix is inserted at the same column for every line
10932 // in a selection.
10933 editor.change_selections(None, cx, |s| {
10934 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10935 });
10936 editor.toggle_comments(&ToggleComments, cx);
10937 assert_eq!(
10938 editor.text(cx),
10939 "
10940 fn a() {
10941 // b();
10942 // c();
10943 // d();
10944 }
10945 "
10946 .unindent()
10947 );
10948
10949 // If a selection ends at the beginning of a line, that line is not toggled.
10950 editor.change_selections(None, cx, |s| {
10951 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10952 });
10953 editor.toggle_comments(&ToggleComments, cx);
10954 assert_eq!(
10955 editor.text(cx),
10956 "
10957 fn a() {
10958 // b();
10959 c();
10960 // d();
10961 }
10962 "
10963 .unindent()
10964 );
10965 });
10966 }
10967
10968 #[gpui::test]
10969 async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
10970 let mut cx = EditorTestContext::new(cx);
10971
10972 let html_language = Arc::new(
10973 Language::new(
10974 LanguageConfig {
10975 name: "HTML".into(),
10976 block_comment: Some(("<!-- ".into(), " -->".into())),
10977 ..Default::default()
10978 },
10979 Some(tree_sitter_html::language()),
10980 )
10981 .with_injection_query(
10982 r#"
10983 (script_element
10984 (raw_text) @content
10985 (#set! "language" "javascript"))
10986 "#,
10987 )
10988 .unwrap(),
10989 );
10990
10991 let javascript_language = Arc::new(Language::new(
10992 LanguageConfig {
10993 name: "JavaScript".into(),
10994 line_comment: Some("// ".into()),
10995 ..Default::default()
10996 },
10997 Some(tree_sitter_javascript::language()),
10998 ));
10999
11000 let registry = Arc::new(LanguageRegistry::test());
11001 registry.add(html_language.clone());
11002 registry.add(javascript_language.clone());
11003
11004 cx.update_buffer(|buffer, cx| {
11005 buffer.set_language_registry(registry);
11006 buffer.set_language(Some(html_language), cx);
11007 });
11008
11009 // Toggle comments for empty selections
11010 cx.set_state(
11011 &r#"
11012 <p>A</p>ˇ
11013 <p>B</p>ˇ
11014 <p>C</p>ˇ
11015 "#
11016 .unindent(),
11017 );
11018 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11019 cx.assert_editor_state(
11020 &r#"
11021 <!-- <p>A</p>ˇ -->
11022 <!-- <p>B</p>ˇ -->
11023 <!-- <p>C</p>ˇ -->
11024 "#
11025 .unindent(),
11026 );
11027 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11028 cx.assert_editor_state(
11029 &r#"
11030 <p>A</p>ˇ
11031 <p>B</p>ˇ
11032 <p>C</p>ˇ
11033 "#
11034 .unindent(),
11035 );
11036
11037 // Toggle comments for mixture of empty and non-empty selections, where
11038 // multiple selections occupy a given line.
11039 cx.set_state(
11040 &r#"
11041 <p>A«</p>
11042 <p>ˇ»B</p>ˇ
11043 <p>C«</p>
11044 <p>ˇ»D</p>ˇ
11045 "#
11046 .unindent(),
11047 );
11048
11049 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11050 cx.assert_editor_state(
11051 &r#"
11052 <!-- <p>A«</p>
11053 <p>ˇ»B</p>ˇ -->
11054 <!-- <p>C«</p>
11055 <p>ˇ»D</p>ˇ -->
11056 "#
11057 .unindent(),
11058 );
11059 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11060 cx.assert_editor_state(
11061 &r#"
11062 <p>A«</p>
11063 <p>ˇ»B</p>ˇ
11064 <p>C«</p>
11065 <p>ˇ»D</p>ˇ
11066 "#
11067 .unindent(),
11068 );
11069
11070 // Toggle comments when different languages are active for different
11071 // selections.
11072 cx.set_state(
11073 &r#"
11074 ˇ<script>
11075 ˇvar x = new Y();
11076 ˇ</script>
11077 "#
11078 .unindent(),
11079 );
11080 cx.foreground().run_until_parked();
11081 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11082 cx.assert_editor_state(
11083 &r#"
11084 <!-- ˇ<script> -->
11085 // ˇvar x = new Y();
11086 <!-- ˇ</script> -->
11087 "#
11088 .unindent(),
11089 );
11090 }
11091
11092 #[gpui::test]
11093 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
11094 cx.set_global(Settings::test(cx));
11095 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11096 let multibuffer = cx.add_model(|cx| {
11097 let mut multibuffer = MultiBuffer::new(0);
11098 multibuffer.push_excerpts(
11099 buffer.clone(),
11100 [
11101 ExcerptRange {
11102 context: Point::new(0, 0)..Point::new(0, 4),
11103 primary: None,
11104 },
11105 ExcerptRange {
11106 context: Point::new(1, 0)..Point::new(1, 4),
11107 primary: None,
11108 },
11109 ],
11110 cx,
11111 );
11112 multibuffer
11113 });
11114
11115 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
11116
11117 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
11118 view.update(cx, |view, cx| {
11119 assert_eq!(view.text(cx), "aaaa\nbbbb");
11120 view.change_selections(None, cx, |s| {
11121 s.select_ranges([
11122 Point::new(0, 0)..Point::new(0, 0),
11123 Point::new(1, 0)..Point::new(1, 0),
11124 ])
11125 });
11126
11127 view.handle_input("X", cx);
11128 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
11129 assert_eq!(
11130 view.selections.ranges(cx),
11131 [
11132 Point::new(0, 1)..Point::new(0, 1),
11133 Point::new(1, 1)..Point::new(1, 1),
11134 ]
11135 )
11136 });
11137 }
11138
11139 #[gpui::test]
11140 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
11141 cx.set_global(Settings::test(cx));
11142 let markers = vec![('[', ']').into(), ('(', ')').into()];
11143 let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11144 indoc! {"
11145 [aaaa
11146 (bbbb]
11147 cccc)",
11148 },
11149 markers.clone(),
11150 );
11151 let excerpt_ranges = markers.into_iter().map(|marker| {
11152 let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11153 ExcerptRange {
11154 context,
11155 primary: None,
11156 }
11157 });
11158 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
11159 let multibuffer = cx.add_model(|cx| {
11160 let mut multibuffer = MultiBuffer::new(0);
11161 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11162 multibuffer
11163 });
11164
11165 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
11166 view.update(cx, |view, cx| {
11167 let (expected_text, selection_ranges) = marked_text_ranges(
11168 indoc! {"
11169 aaaa
11170 bˇbbb
11171 bˇbbˇb
11172 cccc"
11173 },
11174 true,
11175 );
11176 assert_eq!(view.text(cx), expected_text);
11177 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
11178
11179 view.handle_input("X", cx);
11180
11181 let (expected_text, expected_selections) = marked_text_ranges(
11182 indoc! {"
11183 aaaa
11184 bXˇbbXb
11185 bXˇbbXˇb
11186 cccc"
11187 },
11188 false,
11189 );
11190 assert_eq!(view.text(cx), expected_text);
11191 assert_eq!(view.selections.ranges(cx), expected_selections);
11192
11193 view.newline(&Newline, cx);
11194 let (expected_text, expected_selections) = marked_text_ranges(
11195 indoc! {"
11196 aaaa
11197 bX
11198 ˇbbX
11199 b
11200 bX
11201 ˇbbX
11202 ˇb
11203 cccc"
11204 },
11205 false,
11206 );
11207 assert_eq!(view.text(cx), expected_text);
11208 assert_eq!(view.selections.ranges(cx), expected_selections);
11209 });
11210 }
11211
11212 #[gpui::test]
11213 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
11214 cx.set_global(Settings::test(cx));
11215 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11216 let mut excerpt1_id = None;
11217 let multibuffer = cx.add_model(|cx| {
11218 let mut multibuffer = MultiBuffer::new(0);
11219 excerpt1_id = multibuffer
11220 .push_excerpts(
11221 buffer.clone(),
11222 [
11223 ExcerptRange {
11224 context: Point::new(0, 0)..Point::new(1, 4),
11225 primary: None,
11226 },
11227 ExcerptRange {
11228 context: Point::new(1, 0)..Point::new(2, 4),
11229 primary: None,
11230 },
11231 ],
11232 cx,
11233 )
11234 .into_iter()
11235 .next();
11236 multibuffer
11237 });
11238 assert_eq!(
11239 multibuffer.read(cx).read(cx).text(),
11240 "aaaa\nbbbb\nbbbb\ncccc"
11241 );
11242 let (_, editor) = cx.add_window(Default::default(), |cx| {
11243 let mut editor = build_editor(multibuffer.clone(), cx);
11244 let snapshot = editor.snapshot(cx);
11245 editor.change_selections(None, cx, |s| {
11246 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11247 });
11248 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
11249 assert_eq!(
11250 editor.selections.ranges(cx),
11251 [
11252 Point::new(1, 3)..Point::new(1, 3),
11253 Point::new(2, 1)..Point::new(2, 1),
11254 ]
11255 );
11256 editor
11257 });
11258
11259 // Refreshing selections is a no-op when excerpts haven't changed.
11260 editor.update(cx, |editor, cx| {
11261 editor.change_selections(None, cx, |s| {
11262 s.refresh();
11263 });
11264 assert_eq!(
11265 editor.selections.ranges(cx),
11266 [
11267 Point::new(1, 3)..Point::new(1, 3),
11268 Point::new(2, 1)..Point::new(2, 1),
11269 ]
11270 );
11271 });
11272
11273 multibuffer.update(cx, |multibuffer, cx| {
11274 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
11275 });
11276 editor.update(cx, |editor, cx| {
11277 // Removing an excerpt causes the first selection to become degenerate.
11278 assert_eq!(
11279 editor.selections.ranges(cx),
11280 [
11281 Point::new(0, 0)..Point::new(0, 0),
11282 Point::new(0, 1)..Point::new(0, 1)
11283 ]
11284 );
11285
11286 // Refreshing selections will relocate the first selection to the original buffer
11287 // location.
11288 editor.change_selections(None, cx, |s| {
11289 s.refresh();
11290 });
11291 assert_eq!(
11292 editor.selections.ranges(cx),
11293 [
11294 Point::new(0, 1)..Point::new(0, 1),
11295 Point::new(0, 3)..Point::new(0, 3)
11296 ]
11297 );
11298 assert!(editor.selections.pending_anchor().is_some());
11299 });
11300 }
11301
11302 #[gpui::test]
11303 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
11304 cx.set_global(Settings::test(cx));
11305 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11306 let mut excerpt1_id = None;
11307 let multibuffer = cx.add_model(|cx| {
11308 let mut multibuffer = MultiBuffer::new(0);
11309 excerpt1_id = multibuffer
11310 .push_excerpts(
11311 buffer.clone(),
11312 [
11313 ExcerptRange {
11314 context: Point::new(0, 0)..Point::new(1, 4),
11315 primary: None,
11316 },
11317 ExcerptRange {
11318 context: Point::new(1, 0)..Point::new(2, 4),
11319 primary: None,
11320 },
11321 ],
11322 cx,
11323 )
11324 .into_iter()
11325 .next();
11326 multibuffer
11327 });
11328 assert_eq!(
11329 multibuffer.read(cx).read(cx).text(),
11330 "aaaa\nbbbb\nbbbb\ncccc"
11331 );
11332 let (_, editor) = cx.add_window(Default::default(), |cx| {
11333 let mut editor = build_editor(multibuffer.clone(), cx);
11334 let snapshot = editor.snapshot(cx);
11335 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
11336 assert_eq!(
11337 editor.selections.ranges(cx),
11338 [Point::new(1, 3)..Point::new(1, 3)]
11339 );
11340 editor
11341 });
11342
11343 multibuffer.update(cx, |multibuffer, cx| {
11344 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
11345 });
11346 editor.update(cx, |editor, cx| {
11347 assert_eq!(
11348 editor.selections.ranges(cx),
11349 [Point::new(0, 0)..Point::new(0, 0)]
11350 );
11351
11352 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11353 editor.change_selections(None, cx, |s| {
11354 s.refresh();
11355 });
11356 assert_eq!(
11357 editor.selections.ranges(cx),
11358 [Point::new(0, 3)..Point::new(0, 3)]
11359 );
11360 assert!(editor.selections.pending_anchor().is_some());
11361 });
11362 }
11363
11364 #[gpui::test]
11365 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
11366 cx.update(|cx| cx.set_global(Settings::test(cx)));
11367 let language = Arc::new(
11368 Language::new(
11369 LanguageConfig {
11370 brackets: vec![
11371 BracketPair {
11372 start: "{".to_string(),
11373 end: "}".to_string(),
11374 close: true,
11375 newline: true,
11376 },
11377 BracketPair {
11378 start: "/* ".to_string(),
11379 end: " */".to_string(),
11380 close: true,
11381 newline: true,
11382 },
11383 ],
11384 ..Default::default()
11385 },
11386 Some(tree_sitter_rust::language()),
11387 )
11388 .with_indents_query("")
11389 .unwrap(),
11390 );
11391
11392 let text = concat!(
11393 "{ }\n", // Suppress rustfmt
11394 " x\n", //
11395 " /* */\n", //
11396 "x\n", //
11397 "{{} }\n", //
11398 );
11399
11400 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
11401 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
11402 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
11403 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
11404 .await;
11405
11406 view.update(cx, |view, cx| {
11407 view.change_selections(None, cx, |s| {
11408 s.select_display_ranges([
11409 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
11410 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
11411 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
11412 ])
11413 });
11414 view.newline(&Newline, cx);
11415
11416 assert_eq!(
11417 view.buffer().read(cx).read(cx).text(),
11418 concat!(
11419 "{ \n", // Suppress rustfmt
11420 "\n", //
11421 "}\n", //
11422 " x\n", //
11423 " /* \n", //
11424 " \n", //
11425 " */\n", //
11426 "x\n", //
11427 "{{} \n", //
11428 "}\n", //
11429 )
11430 );
11431 });
11432 }
11433
11434 #[gpui::test]
11435 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
11436 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11437
11438 cx.set_global(Settings::test(cx));
11439 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11440
11441 editor.update(cx, |editor, cx| {
11442 struct Type1;
11443 struct Type2;
11444
11445 let buffer = buffer.read(cx).snapshot(cx);
11446
11447 let anchor_range = |range: Range<Point>| {
11448 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
11449 };
11450
11451 editor.highlight_background::<Type1>(
11452 vec![
11453 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11454 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11455 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11456 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11457 ],
11458 |_| Color::red(),
11459 cx,
11460 );
11461 editor.highlight_background::<Type2>(
11462 vec![
11463 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11464 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11465 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11466 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11467 ],
11468 |_| Color::green(),
11469 cx,
11470 );
11471
11472 let snapshot = editor.snapshot(cx);
11473 let mut highlighted_ranges = editor.background_highlights_in_range(
11474 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11475 &snapshot,
11476 cx.global::<Settings>().theme.as_ref(),
11477 );
11478 // Enforce a consistent ordering based on color without relying on the ordering of the
11479 // highlight's `TypeId` which is non-deterministic.
11480 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11481 assert_eq!(
11482 highlighted_ranges,
11483 &[
11484 (
11485 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
11486 Color::green(),
11487 ),
11488 (
11489 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
11490 Color::green(),
11491 ),
11492 (
11493 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
11494 Color::red(),
11495 ),
11496 (
11497 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11498 Color::red(),
11499 ),
11500 ]
11501 );
11502 assert_eq!(
11503 editor.background_highlights_in_range(
11504 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11505 &snapshot,
11506 cx.global::<Settings>().theme.as_ref(),
11507 ),
11508 &[(
11509 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11510 Color::red(),
11511 )]
11512 );
11513 });
11514 }
11515
11516 #[gpui::test]
11517 fn test_following(cx: &mut gpui::MutableAppContext) {
11518 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11519
11520 cx.set_global(Settings::test(cx));
11521
11522 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11523 let (_, follower) = cx.add_window(
11524 WindowOptions {
11525 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
11526 ..Default::default()
11527 },
11528 |cx| build_editor(buffer.clone(), cx),
11529 );
11530
11531 let pending_update = Rc::new(RefCell::new(None));
11532 follower.update(cx, {
11533 let update = pending_update.clone();
11534 |_, cx| {
11535 cx.subscribe(&leader, move |_, leader, event, cx| {
11536 leader
11537 .read(cx)
11538 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
11539 })
11540 .detach();
11541 }
11542 });
11543
11544 // Update the selections only
11545 leader.update(cx, |leader, cx| {
11546 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11547 });
11548 follower.update(cx, |follower, cx| {
11549 follower
11550 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11551 .unwrap();
11552 });
11553 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
11554
11555 // Update the scroll position only
11556 leader.update(cx, |leader, cx| {
11557 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11558 });
11559 follower.update(cx, |follower, cx| {
11560 follower
11561 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11562 .unwrap();
11563 });
11564 assert_eq!(
11565 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
11566 vec2f(1.5, 3.5)
11567 );
11568
11569 // Update the selections and scroll position
11570 leader.update(cx, |leader, cx| {
11571 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
11572 leader.request_autoscroll(Autoscroll::Newest, cx);
11573 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11574 });
11575 follower.update(cx, |follower, cx| {
11576 let initial_scroll_position = follower.scroll_position(cx);
11577 follower
11578 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11579 .unwrap();
11580 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
11581 assert!(follower.autoscroll_request.is_some());
11582 });
11583 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
11584
11585 // Creating a pending selection that precedes another selection
11586 leader.update(cx, |leader, cx| {
11587 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11588 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
11589 });
11590 follower.update(cx, |follower, cx| {
11591 follower
11592 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11593 .unwrap();
11594 });
11595 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
11596
11597 // Extend the pending selection so that it surrounds another selection
11598 leader.update(cx, |leader, cx| {
11599 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
11600 });
11601 follower.update(cx, |follower, cx| {
11602 follower
11603 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11604 .unwrap();
11605 });
11606 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
11607 }
11608
11609 #[test]
11610 fn test_combine_syntax_and_fuzzy_match_highlights() {
11611 let string = "abcdefghijklmnop";
11612 let syntax_ranges = [
11613 (
11614 0..3,
11615 HighlightStyle {
11616 color: Some(Color::red()),
11617 ..Default::default()
11618 },
11619 ),
11620 (
11621 4..8,
11622 HighlightStyle {
11623 color: Some(Color::green()),
11624 ..Default::default()
11625 },
11626 ),
11627 ];
11628 let match_indices = [4, 6, 7, 8];
11629 assert_eq!(
11630 combine_syntax_and_fuzzy_match_highlights(
11631 string,
11632 Default::default(),
11633 syntax_ranges.into_iter(),
11634 &match_indices,
11635 ),
11636 &[
11637 (
11638 0..3,
11639 HighlightStyle {
11640 color: Some(Color::red()),
11641 ..Default::default()
11642 },
11643 ),
11644 (
11645 4..5,
11646 HighlightStyle {
11647 color: Some(Color::green()),
11648 weight: Some(fonts::Weight::BOLD),
11649 ..Default::default()
11650 },
11651 ),
11652 (
11653 5..6,
11654 HighlightStyle {
11655 color: Some(Color::green()),
11656 ..Default::default()
11657 },
11658 ),
11659 (
11660 6..8,
11661 HighlightStyle {
11662 color: Some(Color::green()),
11663 weight: Some(fonts::Weight::BOLD),
11664 ..Default::default()
11665 },
11666 ),
11667 (
11668 8..9,
11669 HighlightStyle {
11670 weight: Some(fonts::Weight::BOLD),
11671 ..Default::default()
11672 },
11673 ),
11674 ]
11675 );
11676 }
11677
11678 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11679 let point = DisplayPoint::new(row as u32, column as u32);
11680 point..point
11681 }
11682
11683 fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11684 let (text, ranges) = marked_text_ranges(marked_text, true);
11685 assert_eq!(view.text(cx), text);
11686 assert_eq!(
11687 view.selections.ranges(cx),
11688 ranges,
11689 "Assert selections are {}",
11690 marked_text
11691 );
11692 }
11693}
11694
11695trait RangeExt<T> {
11696 fn sorted(&self) -> Range<T>;
11697 fn to_inclusive(&self) -> RangeInclusive<T>;
11698}
11699
11700impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11701 fn sorted(&self) -> Self {
11702 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11703 }
11704
11705 fn to_inclusive(&self) -> RangeInclusive<T> {
11706 self.start.clone()..=self.end.clone()
11707 }
11708}
11709
11710trait RangeToAnchorExt {
11711 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11712}
11713
11714impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11715 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11716 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11717 }
11718}