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_at(start) {
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
2931 .language_at(line_buffer_range.start)
2932 .map(|language| language.name());
2933 let indent_len = match indent_size.kind {
2934 IndentKind::Space => {
2935 cx.global::<Settings>().tab_size(language_name.as_deref())
2936 }
2937 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
2938 };
2939 if old_head.column <= indent_size.len && old_head.column > 0 {
2940 let indent_len = indent_len.get();
2941 new_head = cmp::min(
2942 new_head,
2943 Point::new(
2944 old_head.row,
2945 ((old_head.column - 1) / indent_len) * indent_len,
2946 ),
2947 );
2948 }
2949 }
2950
2951 selection.set_head(new_head, SelectionGoal::None);
2952 }
2953 }
2954 }
2955
2956 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2957 this.insert("", cx);
2958 });
2959 }
2960
2961 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2962 self.transact(cx, |this, cx| {
2963 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2964 let line_mode = s.line_mode;
2965 s.move_with(|map, selection| {
2966 if selection.is_empty() && !line_mode {
2967 let cursor = movement::right(map, selection.head());
2968 selection.set_head(cursor, SelectionGoal::None);
2969 }
2970 })
2971 });
2972 this.insert("", cx);
2973 });
2974 }
2975
2976 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2977 if self.move_to_prev_snippet_tabstop(cx) {
2978 return;
2979 }
2980
2981 self.outdent(&Outdent, cx);
2982 }
2983
2984 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2985 if self.move_to_next_snippet_tabstop(cx) {
2986 return;
2987 }
2988
2989 let mut selections = self.selections.all_adjusted(cx);
2990 let buffer = self.buffer.read(cx);
2991 let snapshot = buffer.snapshot(cx);
2992 let rows_iter = selections.iter().map(|s| s.head().row);
2993 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
2994
2995 let mut edits = Vec::new();
2996 let mut prev_edited_row = 0;
2997 let mut row_delta = 0;
2998 for selection in &mut selections {
2999 if selection.start.row != prev_edited_row {
3000 row_delta = 0;
3001 }
3002 prev_edited_row = selection.end.row;
3003
3004 // If the selection is non-empty, then increase the indentation of the selected lines.
3005 if !selection.is_empty() {
3006 row_delta =
3007 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3008 continue;
3009 }
3010
3011 // If the selection is empty and the cursor is in the leading whitespace before the
3012 // suggested indentation, then auto-indent the line.
3013 let cursor = selection.head();
3014 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3015 let current_indent = snapshot.indent_size_for_line(cursor.row);
3016 if cursor.column < suggested_indent.len
3017 && cursor.column <= current_indent.len
3018 && current_indent.len <= suggested_indent.len
3019 {
3020 selection.start = Point::new(cursor.row, suggested_indent.len);
3021 selection.end = selection.start;
3022 if row_delta == 0 {
3023 edits.extend(Buffer::edit_for_indent_size_adjustment(
3024 cursor.row,
3025 current_indent,
3026 suggested_indent,
3027 ));
3028 row_delta = suggested_indent.len - current_indent.len;
3029 }
3030 continue;
3031 }
3032 }
3033
3034 // Otherwise, insert a hard or soft tab.
3035 let settings = cx.global::<Settings>();
3036 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3037 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3038 IndentSize::tab()
3039 } else {
3040 let tab_size = settings.tab_size(language_name.as_deref()).get();
3041 let char_column = snapshot
3042 .text_for_range(Point::new(cursor.row, 0)..cursor)
3043 .flat_map(str::chars)
3044 .count()
3045 + row_delta as usize;
3046 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3047 IndentSize::spaces(chars_to_next_tab_stop)
3048 };
3049 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3050 selection.end = selection.start;
3051 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3052 row_delta += tab_size.len;
3053 }
3054
3055 self.transact(cx, |this, cx| {
3056 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3057 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections))
3058 });
3059 }
3060
3061 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3062 let mut selections = self.selections.all::<Point>(cx);
3063 let mut prev_edited_row = 0;
3064 let mut row_delta = 0;
3065 let mut edits = Vec::new();
3066 let buffer = self.buffer.read(cx);
3067 let snapshot = buffer.snapshot(cx);
3068 for selection in &mut selections {
3069 if selection.start.row != prev_edited_row {
3070 row_delta = 0;
3071 }
3072 prev_edited_row = selection.end.row;
3073
3074 row_delta =
3075 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3076 }
3077
3078 self.transact(cx, |this, cx| {
3079 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3080 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3081 });
3082 }
3083
3084 fn indent_selection(
3085 buffer: &MultiBuffer,
3086 snapshot: &MultiBufferSnapshot,
3087 selection: &mut Selection<Point>,
3088 edits: &mut Vec<(Range<Point>, String)>,
3089 delta_for_start_row: u32,
3090 cx: &AppContext,
3091 ) -> u32 {
3092 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3093 let settings = cx.global::<Settings>();
3094 let tab_size = settings.tab_size(language_name.as_deref()).get();
3095 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3096 IndentKind::Tab
3097 } else {
3098 IndentKind::Space
3099 };
3100 let mut start_row = selection.start.row;
3101 let mut end_row = selection.end.row + 1;
3102
3103 // If a selection ends at the beginning of a line, don't indent
3104 // that last line.
3105 if selection.end.column == 0 {
3106 end_row -= 1;
3107 }
3108
3109 // Avoid re-indenting a row that has already been indented by a
3110 // previous selection, but still update this selection's column
3111 // to reflect that indentation.
3112 if delta_for_start_row > 0 {
3113 start_row += 1;
3114 selection.start.column += delta_for_start_row;
3115 if selection.end.row == selection.start.row {
3116 selection.end.column += delta_for_start_row;
3117 }
3118 }
3119
3120 let mut delta_for_end_row = 0;
3121 for row in start_row..end_row {
3122 let current_indent = snapshot.indent_size_for_line(row);
3123 let indent_delta = match (current_indent.kind, indent_kind) {
3124 (IndentKind::Space, IndentKind::Space) => {
3125 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3126 IndentSize::spaces(columns_to_next_tab_stop)
3127 }
3128 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3129 (_, IndentKind::Tab) => IndentSize::tab(),
3130 };
3131
3132 let row_start = Point::new(row, 0);
3133 edits.push((
3134 row_start..row_start,
3135 indent_delta.chars().collect::<String>(),
3136 ));
3137
3138 // Update this selection's endpoints to reflect the indentation.
3139 if row == selection.start.row {
3140 selection.start.column += indent_delta.len;
3141 }
3142 if row == selection.end.row {
3143 selection.end.column += indent_delta.len;
3144 delta_for_end_row = indent_delta.len;
3145 }
3146 }
3147
3148 if selection.start.row == selection.end.row {
3149 delta_for_start_row + delta_for_end_row
3150 } else {
3151 delta_for_end_row
3152 }
3153 }
3154
3155 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3156 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3157 let selections = self.selections.all::<Point>(cx);
3158 let mut deletion_ranges = Vec::new();
3159 let mut last_outdent = None;
3160 {
3161 let buffer = self.buffer.read(cx);
3162 let snapshot = buffer.snapshot(cx);
3163 for selection in &selections {
3164 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3165 let tab_size = cx
3166 .global::<Settings>()
3167 .tab_size(language_name.as_deref())
3168 .get();
3169 let mut rows = selection.spanned_rows(false, &display_map);
3170
3171 // Avoid re-outdenting a row that has already been outdented by a
3172 // previous selection.
3173 if let Some(last_row) = last_outdent {
3174 if last_row == rows.start {
3175 rows.start += 1;
3176 }
3177 }
3178
3179 for row in rows {
3180 let indent_size = snapshot.indent_size_for_line(row);
3181 if indent_size.len > 0 {
3182 let deletion_len = match indent_size.kind {
3183 IndentKind::Space => {
3184 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3185 if columns_to_prev_tab_stop == 0 {
3186 tab_size
3187 } else {
3188 columns_to_prev_tab_stop
3189 }
3190 }
3191 IndentKind::Tab => 1,
3192 };
3193 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3194 last_outdent = Some(row);
3195 }
3196 }
3197 }
3198 }
3199
3200 self.transact(cx, |this, cx| {
3201 this.buffer.update(cx, |buffer, cx| {
3202 let empty_str: Arc<str> = "".into();
3203 buffer.edit(
3204 deletion_ranges
3205 .into_iter()
3206 .map(|range| (range, empty_str.clone())),
3207 None,
3208 cx,
3209 );
3210 });
3211 let selections = this.selections.all::<usize>(cx);
3212 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3213 });
3214 }
3215
3216 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3217 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3218 let selections = self.selections.all::<Point>(cx);
3219
3220 let mut new_cursors = Vec::new();
3221 let mut edit_ranges = Vec::new();
3222 let mut selections = selections.iter().peekable();
3223 while let Some(selection) = selections.next() {
3224 let mut rows = selection.spanned_rows(false, &display_map);
3225 let goal_display_column = selection.head().to_display_point(&display_map).column();
3226
3227 // Accumulate contiguous regions of rows that we want to delete.
3228 while let Some(next_selection) = selections.peek() {
3229 let next_rows = next_selection.spanned_rows(false, &display_map);
3230 if next_rows.start <= rows.end {
3231 rows.end = next_rows.end;
3232 selections.next().unwrap();
3233 } else {
3234 break;
3235 }
3236 }
3237
3238 let buffer = &display_map.buffer_snapshot;
3239 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3240 let edit_end;
3241 let cursor_buffer_row;
3242 if buffer.max_point().row >= rows.end {
3243 // If there's a line after the range, delete the \n from the end of the row range
3244 // and position the cursor on the next line.
3245 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3246 cursor_buffer_row = rows.end;
3247 } else {
3248 // If there isn't a line after the range, delete the \n from the line before the
3249 // start of the row range and position the cursor there.
3250 edit_start = edit_start.saturating_sub(1);
3251 edit_end = buffer.len();
3252 cursor_buffer_row = rows.start.saturating_sub(1);
3253 }
3254
3255 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3256 *cursor.column_mut() =
3257 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3258
3259 new_cursors.push((
3260 selection.id,
3261 buffer.anchor_after(cursor.to_point(&display_map)),
3262 ));
3263 edit_ranges.push(edit_start..edit_end);
3264 }
3265
3266 self.transact(cx, |this, cx| {
3267 let buffer = this.buffer.update(cx, |buffer, cx| {
3268 let empty_str: Arc<str> = "".into();
3269 buffer.edit(
3270 edit_ranges
3271 .into_iter()
3272 .map(|range| (range, empty_str.clone())),
3273 None,
3274 cx,
3275 );
3276 buffer.snapshot(cx)
3277 });
3278 let new_selections = new_cursors
3279 .into_iter()
3280 .map(|(id, cursor)| {
3281 let cursor = cursor.to_point(&buffer);
3282 Selection {
3283 id,
3284 start: cursor,
3285 end: cursor,
3286 reversed: false,
3287 goal: SelectionGoal::None,
3288 }
3289 })
3290 .collect();
3291
3292 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3293 s.select(new_selections);
3294 });
3295 });
3296 }
3297
3298 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3299 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3300 let buffer = &display_map.buffer_snapshot;
3301 let selections = self.selections.all::<Point>(cx);
3302
3303 let mut edits = Vec::new();
3304 let mut selections_iter = selections.iter().peekable();
3305 while let Some(selection) = selections_iter.next() {
3306 // Avoid duplicating the same lines twice.
3307 let mut rows = selection.spanned_rows(false, &display_map);
3308
3309 while let Some(next_selection) = selections_iter.peek() {
3310 let next_rows = next_selection.spanned_rows(false, &display_map);
3311 if next_rows.start < rows.end {
3312 rows.end = next_rows.end;
3313 selections_iter.next().unwrap();
3314 } else {
3315 break;
3316 }
3317 }
3318
3319 // Copy the text from the selected row region and splice it at the start of the region.
3320 let start = Point::new(rows.start, 0);
3321 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3322 let text = buffer
3323 .text_for_range(start..end)
3324 .chain(Some("\n"))
3325 .collect::<String>();
3326 edits.push((start..start, text));
3327 }
3328
3329 self.transact(cx, |this, cx| {
3330 this.buffer.update(cx, |buffer, cx| {
3331 buffer.edit(edits, None, cx);
3332 });
3333
3334 this.request_autoscroll(Autoscroll::Fit, cx);
3335 });
3336 }
3337
3338 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3339 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3340 let buffer = self.buffer.read(cx).snapshot(cx);
3341
3342 let mut edits = Vec::new();
3343 let mut unfold_ranges = Vec::new();
3344 let mut refold_ranges = Vec::new();
3345
3346 let selections = self.selections.all::<Point>(cx);
3347 let mut selections = selections.iter().peekable();
3348 let mut contiguous_row_selections = Vec::new();
3349 let mut new_selections = Vec::new();
3350
3351 while let Some(selection) = selections.next() {
3352 // Find all the selections that span a contiguous row range
3353 contiguous_row_selections.push(selection.clone());
3354 let start_row = selection.start.row;
3355 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3356 display_map.next_line_boundary(selection.end).0.row + 1
3357 } else {
3358 selection.end.row
3359 };
3360
3361 while let Some(next_selection) = selections.peek() {
3362 if next_selection.start.row <= end_row {
3363 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3364 display_map.next_line_boundary(next_selection.end).0.row + 1
3365 } else {
3366 next_selection.end.row
3367 };
3368 contiguous_row_selections.push(selections.next().unwrap().clone());
3369 } else {
3370 break;
3371 }
3372 }
3373
3374 // Move the text spanned by the row range to be before the line preceding the row range
3375 if start_row > 0 {
3376 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3377 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3378 let insertion_point = display_map
3379 .prev_line_boundary(Point::new(start_row - 1, 0))
3380 .0;
3381
3382 // Don't move lines across excerpts
3383 if buffer
3384 .excerpt_boundaries_in_range((
3385 Bound::Excluded(insertion_point),
3386 Bound::Included(range_to_move.end),
3387 ))
3388 .next()
3389 .is_none()
3390 {
3391 let text = buffer
3392 .text_for_range(range_to_move.clone())
3393 .flat_map(|s| s.chars())
3394 .skip(1)
3395 .chain(['\n'])
3396 .collect::<String>();
3397
3398 edits.push((
3399 buffer.anchor_after(range_to_move.start)
3400 ..buffer.anchor_before(range_to_move.end),
3401 String::new(),
3402 ));
3403 let insertion_anchor = buffer.anchor_after(insertion_point);
3404 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3405
3406 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3407
3408 // Move selections up
3409 new_selections.extend(contiguous_row_selections.drain(..).map(
3410 |mut selection| {
3411 selection.start.row -= row_delta;
3412 selection.end.row -= row_delta;
3413 selection
3414 },
3415 ));
3416
3417 // Move folds up
3418 unfold_ranges.push(range_to_move.clone());
3419 for fold in display_map.folds_in_range(
3420 buffer.anchor_before(range_to_move.start)
3421 ..buffer.anchor_after(range_to_move.end),
3422 ) {
3423 let mut start = fold.start.to_point(&buffer);
3424 let mut end = fold.end.to_point(&buffer);
3425 start.row -= row_delta;
3426 end.row -= row_delta;
3427 refold_ranges.push(start..end);
3428 }
3429 }
3430 }
3431
3432 // If we didn't move line(s), preserve the existing selections
3433 new_selections.append(&mut contiguous_row_selections);
3434 }
3435
3436 self.transact(cx, |this, cx| {
3437 this.unfold_ranges(unfold_ranges, true, cx);
3438 this.buffer.update(cx, |buffer, cx| {
3439 for (range, text) in edits {
3440 buffer.edit([(range, text)], None, cx);
3441 }
3442 });
3443 this.fold_ranges(refold_ranges, cx);
3444 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3445 s.select(new_selections);
3446 })
3447 });
3448 }
3449
3450 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3451 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3452 let buffer = self.buffer.read(cx).snapshot(cx);
3453
3454 let mut edits = Vec::new();
3455 let mut unfold_ranges = Vec::new();
3456 let mut refold_ranges = Vec::new();
3457
3458 let selections = self.selections.all::<Point>(cx);
3459 let mut selections = selections.iter().peekable();
3460 let mut contiguous_row_selections = Vec::new();
3461 let mut new_selections = Vec::new();
3462
3463 while let Some(selection) = selections.next() {
3464 // Find all the selections that span a contiguous row range
3465 contiguous_row_selections.push(selection.clone());
3466 let start_row = selection.start.row;
3467 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3468 display_map.next_line_boundary(selection.end).0.row + 1
3469 } else {
3470 selection.end.row
3471 };
3472
3473 while let Some(next_selection) = selections.peek() {
3474 if next_selection.start.row <= end_row {
3475 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3476 display_map.next_line_boundary(next_selection.end).0.row + 1
3477 } else {
3478 next_selection.end.row
3479 };
3480 contiguous_row_selections.push(selections.next().unwrap().clone());
3481 } else {
3482 break;
3483 }
3484 }
3485
3486 // Move the text spanned by the row range to be after the last line of the row range
3487 if end_row <= buffer.max_point().row {
3488 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3489 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3490
3491 // Don't move lines across excerpt boundaries
3492 if buffer
3493 .excerpt_boundaries_in_range((
3494 Bound::Excluded(range_to_move.start),
3495 Bound::Included(insertion_point),
3496 ))
3497 .next()
3498 .is_none()
3499 {
3500 let mut text = String::from("\n");
3501 text.extend(buffer.text_for_range(range_to_move.clone()));
3502 text.pop(); // Drop trailing newline
3503 edits.push((
3504 buffer.anchor_after(range_to_move.start)
3505 ..buffer.anchor_before(range_to_move.end),
3506 String::new(),
3507 ));
3508 let insertion_anchor = buffer.anchor_after(insertion_point);
3509 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3510
3511 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3512
3513 // Move selections down
3514 new_selections.extend(contiguous_row_selections.drain(..).map(
3515 |mut selection| {
3516 selection.start.row += row_delta;
3517 selection.end.row += row_delta;
3518 selection
3519 },
3520 ));
3521
3522 // Move folds down
3523 unfold_ranges.push(range_to_move.clone());
3524 for fold in display_map.folds_in_range(
3525 buffer.anchor_before(range_to_move.start)
3526 ..buffer.anchor_after(range_to_move.end),
3527 ) {
3528 let mut start = fold.start.to_point(&buffer);
3529 let mut end = fold.end.to_point(&buffer);
3530 start.row += row_delta;
3531 end.row += row_delta;
3532 refold_ranges.push(start..end);
3533 }
3534 }
3535 }
3536
3537 // If we didn't move line(s), preserve the existing selections
3538 new_selections.append(&mut contiguous_row_selections);
3539 }
3540
3541 self.transact(cx, |this, cx| {
3542 this.unfold_ranges(unfold_ranges, true, cx);
3543 this.buffer.update(cx, |buffer, cx| {
3544 for (range, text) in edits {
3545 buffer.edit([(range, text)], None, cx);
3546 }
3547 });
3548 this.fold_ranges(refold_ranges, cx);
3549 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3550 });
3551 }
3552
3553 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3554 self.transact(cx, |this, cx| {
3555 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3556 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3557 let line_mode = s.line_mode;
3558 s.move_with(|display_map, selection| {
3559 if !selection.is_empty() || line_mode {
3560 return;
3561 }
3562
3563 let mut head = selection.head();
3564 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3565 if head.column() == display_map.line_len(head.row()) {
3566 transpose_offset = display_map
3567 .buffer_snapshot
3568 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3569 }
3570
3571 if transpose_offset == 0 {
3572 return;
3573 }
3574
3575 *head.column_mut() += 1;
3576 head = display_map.clip_point(head, Bias::Right);
3577 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3578
3579 let transpose_start = display_map
3580 .buffer_snapshot
3581 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3582 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3583 let transpose_end = display_map
3584 .buffer_snapshot
3585 .clip_offset(transpose_offset + 1, Bias::Right);
3586 if let Some(ch) =
3587 display_map.buffer_snapshot.chars_at(transpose_start).next()
3588 {
3589 edits.push((transpose_start..transpose_offset, String::new()));
3590 edits.push((transpose_end..transpose_end, ch.to_string()));
3591 }
3592 }
3593 });
3594 edits
3595 });
3596 this.buffer
3597 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3598 let selections = this.selections.all::<usize>(cx);
3599 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3600 s.select(selections);
3601 });
3602 });
3603 }
3604
3605 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3606 let mut text = String::new();
3607 let buffer = self.buffer.read(cx).snapshot(cx);
3608 let mut selections = self.selections.all::<Point>(cx);
3609 let mut clipboard_selections = Vec::with_capacity(selections.len());
3610 {
3611 let max_point = buffer.max_point();
3612 for selection in &mut selections {
3613 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3614 if is_entire_line {
3615 selection.start = Point::new(selection.start.row, 0);
3616 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3617 selection.goal = SelectionGoal::None;
3618 }
3619 let mut len = 0;
3620 for chunk in buffer.text_for_range(selection.start..selection.end) {
3621 text.push_str(chunk);
3622 len += chunk.len();
3623 }
3624 clipboard_selections.push(ClipboardSelection {
3625 len,
3626 is_entire_line,
3627 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3628 });
3629 }
3630 }
3631
3632 self.transact(cx, |this, cx| {
3633 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3634 s.select(selections);
3635 });
3636 this.insert("", cx);
3637 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3638 });
3639 }
3640
3641 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3642 let selections = self.selections.all::<Point>(cx);
3643 let buffer = self.buffer.read(cx).read(cx);
3644 let mut text = String::new();
3645
3646 let mut clipboard_selections = Vec::with_capacity(selections.len());
3647 {
3648 let max_point = buffer.max_point();
3649 for selection in selections.iter() {
3650 let mut start = selection.start;
3651 let mut end = selection.end;
3652 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3653 if is_entire_line {
3654 start = Point::new(start.row, 0);
3655 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3656 }
3657 let mut len = 0;
3658 for chunk in buffer.text_for_range(start..end) {
3659 text.push_str(chunk);
3660 len += chunk.len();
3661 }
3662 clipboard_selections.push(ClipboardSelection {
3663 len,
3664 is_entire_line,
3665 first_line_indent: buffer.indent_size_for_line(start.row).len,
3666 });
3667 }
3668 }
3669
3670 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3671 }
3672
3673 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3674 self.transact(cx, |this, cx| {
3675 if let Some(item) = cx.as_mut().read_from_clipboard() {
3676 let mut clipboard_text = Cow::Borrowed(item.text());
3677 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3678 let old_selections = this.selections.all::<usize>(cx);
3679 let all_selections_were_entire_line =
3680 clipboard_selections.iter().all(|s| s.is_entire_line);
3681 let first_selection_indent_column =
3682 clipboard_selections.first().map(|s| s.first_line_indent);
3683 if clipboard_selections.len() != old_selections.len() {
3684 let mut newline_separated_text = String::new();
3685 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3686 let mut ix = 0;
3687 while let Some(clipboard_selection) = clipboard_selections.next() {
3688 newline_separated_text
3689 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3690 ix += clipboard_selection.len;
3691 if clipboard_selections.peek().is_some() {
3692 newline_separated_text.push('\n');
3693 }
3694 }
3695 clipboard_text = Cow::Owned(newline_separated_text);
3696 }
3697
3698 this.buffer.update(cx, |buffer, cx| {
3699 let snapshot = buffer.read(cx);
3700 let mut start_offset = 0;
3701 let mut edits = Vec::new();
3702 let mut original_indent_columns = Vec::new();
3703 let line_mode = this.selections.line_mode;
3704 for (ix, selection) in old_selections.iter().enumerate() {
3705 let to_insert;
3706 let entire_line;
3707 let original_indent_column;
3708 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3709 let end_offset = start_offset + clipboard_selection.len;
3710 to_insert = &clipboard_text[start_offset..end_offset];
3711 entire_line = clipboard_selection.is_entire_line;
3712 start_offset = end_offset;
3713 original_indent_column =
3714 Some(clipboard_selection.first_line_indent);
3715 } else {
3716 to_insert = clipboard_text.as_str();
3717 entire_line = all_selections_were_entire_line;
3718 original_indent_column = first_selection_indent_column
3719 }
3720
3721 // If the corresponding selection was empty when this slice of the
3722 // clipboard text was written, then the entire line containing the
3723 // selection was copied. If this selection is also currently empty,
3724 // then paste the line before the current line of the buffer.
3725 let range = if selection.is_empty() && !line_mode && entire_line {
3726 let column = selection.start.to_point(&snapshot).column as usize;
3727 let line_start = selection.start - column;
3728 line_start..line_start
3729 } else {
3730 selection.range()
3731 };
3732
3733 edits.push((range, to_insert));
3734 original_indent_columns.extend(original_indent_column);
3735 }
3736 drop(snapshot);
3737
3738 buffer.edit(
3739 edits,
3740 Some(AutoindentMode::Block {
3741 original_indent_columns,
3742 }),
3743 cx,
3744 );
3745 });
3746
3747 let selections = this.selections.all::<usize>(cx);
3748 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3749 } else {
3750 this.insert(&clipboard_text, cx);
3751 }
3752 }
3753 });
3754 }
3755
3756 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3757 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3758 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3759 self.change_selections(None, cx, |s| {
3760 s.select_anchors(selections.to_vec());
3761 });
3762 }
3763 self.request_autoscroll(Autoscroll::Fit, cx);
3764 self.unmark_text(cx);
3765 cx.emit(Event::Edited);
3766 }
3767 }
3768
3769 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3770 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3771 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3772 {
3773 self.change_selections(None, cx, |s| {
3774 s.select_anchors(selections.to_vec());
3775 });
3776 }
3777 self.request_autoscroll(Autoscroll::Fit, cx);
3778 self.unmark_text(cx);
3779 cx.emit(Event::Edited);
3780 }
3781 }
3782
3783 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3784 self.buffer
3785 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3786 }
3787
3788 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3789 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3790 let line_mode = s.line_mode;
3791 s.move_with(|map, selection| {
3792 let cursor = if selection.is_empty() && !line_mode {
3793 movement::left(map, selection.start)
3794 } else {
3795 selection.start
3796 };
3797 selection.collapse_to(cursor, SelectionGoal::None);
3798 });
3799 })
3800 }
3801
3802 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3803 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3804 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3805 })
3806 }
3807
3808 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3809 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3810 let line_mode = s.line_mode;
3811 s.move_with(|map, selection| {
3812 let cursor = if selection.is_empty() && !line_mode {
3813 movement::right(map, selection.end)
3814 } else {
3815 selection.end
3816 };
3817 selection.collapse_to(cursor, SelectionGoal::None)
3818 });
3819 })
3820 }
3821
3822 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3823 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3824 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3825 })
3826 }
3827
3828 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3829 if self.take_rename(true, cx).is_some() {
3830 return;
3831 }
3832
3833 if let Some(context_menu) = self.context_menu.as_mut() {
3834 if context_menu.select_prev(cx) {
3835 return;
3836 }
3837 }
3838
3839 if matches!(self.mode, EditorMode::SingleLine) {
3840 cx.propagate_action();
3841 return;
3842 }
3843
3844 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3845 let line_mode = s.line_mode;
3846 s.move_with(|map, selection| {
3847 if !selection.is_empty() && !line_mode {
3848 selection.goal = SelectionGoal::None;
3849 }
3850 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
3851 selection.collapse_to(cursor, goal);
3852 });
3853 })
3854 }
3855
3856 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3857 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3858 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3859 })
3860 }
3861
3862 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3863 self.take_rename(true, cx);
3864
3865 if let Some(context_menu) = self.context_menu.as_mut() {
3866 if context_menu.select_next(cx) {
3867 return;
3868 }
3869 }
3870
3871 if matches!(self.mode, EditorMode::SingleLine) {
3872 cx.propagate_action();
3873 return;
3874 }
3875
3876 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3877 let line_mode = s.line_mode;
3878 s.move_with(|map, selection| {
3879 if !selection.is_empty() && !line_mode {
3880 selection.goal = SelectionGoal::None;
3881 }
3882 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
3883 selection.collapse_to(cursor, goal);
3884 });
3885 });
3886 }
3887
3888 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3889 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3890 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3891 });
3892 }
3893
3894 pub fn move_to_previous_word_start(
3895 &mut self,
3896 _: &MoveToPreviousWordStart,
3897 cx: &mut ViewContext<Self>,
3898 ) {
3899 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3900 s.move_cursors_with(|map, head, _| {
3901 (
3902 movement::previous_word_start(map, head),
3903 SelectionGoal::None,
3904 )
3905 });
3906 })
3907 }
3908
3909 pub fn move_to_previous_subword_start(
3910 &mut self,
3911 _: &MoveToPreviousSubwordStart,
3912 cx: &mut ViewContext<Self>,
3913 ) {
3914 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3915 s.move_cursors_with(|map, head, _| {
3916 (
3917 movement::previous_subword_start(map, head),
3918 SelectionGoal::None,
3919 )
3920 });
3921 })
3922 }
3923
3924 pub fn select_to_previous_word_start(
3925 &mut self,
3926 _: &SelectToPreviousWordStart,
3927 cx: &mut ViewContext<Self>,
3928 ) {
3929 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3930 s.move_heads_with(|map, head, _| {
3931 (
3932 movement::previous_word_start(map, head),
3933 SelectionGoal::None,
3934 )
3935 });
3936 })
3937 }
3938
3939 pub fn select_to_previous_subword_start(
3940 &mut self,
3941 _: &SelectToPreviousSubwordStart,
3942 cx: &mut ViewContext<Self>,
3943 ) {
3944 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3945 s.move_heads_with(|map, head, _| {
3946 (
3947 movement::previous_subword_start(map, head),
3948 SelectionGoal::None,
3949 )
3950 });
3951 })
3952 }
3953
3954 pub fn delete_to_previous_word_start(
3955 &mut self,
3956 _: &DeleteToPreviousWordStart,
3957 cx: &mut ViewContext<Self>,
3958 ) {
3959 self.transact(cx, |this, cx| {
3960 this.select_autoclose_pair(cx);
3961 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3962 let line_mode = s.line_mode;
3963 s.move_with(|map, selection| {
3964 if selection.is_empty() && !line_mode {
3965 let cursor = movement::previous_word_start(map, selection.head());
3966 selection.set_head(cursor, SelectionGoal::None);
3967 }
3968 });
3969 });
3970 this.insert("", cx);
3971 });
3972 }
3973
3974 pub fn delete_to_previous_subword_start(
3975 &mut self,
3976 _: &DeleteToPreviousSubwordStart,
3977 cx: &mut ViewContext<Self>,
3978 ) {
3979 self.transact(cx, |this, cx| {
3980 this.select_autoclose_pair(cx);
3981 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3982 let line_mode = s.line_mode;
3983 s.move_with(|map, selection| {
3984 if selection.is_empty() && !line_mode {
3985 let cursor = movement::previous_subword_start(map, selection.head());
3986 selection.set_head(cursor, SelectionGoal::None);
3987 }
3988 });
3989 });
3990 this.insert("", cx);
3991 });
3992 }
3993
3994 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3995 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3996 s.move_cursors_with(|map, head, _| {
3997 (movement::next_word_end(map, head), SelectionGoal::None)
3998 });
3999 })
4000 }
4001
4002 pub fn move_to_next_subword_end(
4003 &mut self,
4004 _: &MoveToNextSubwordEnd,
4005 cx: &mut ViewContext<Self>,
4006 ) {
4007 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4008 s.move_cursors_with(|map, head, _| {
4009 (movement::next_subword_end(map, head), SelectionGoal::None)
4010 });
4011 })
4012 }
4013
4014 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4015 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4016 s.move_heads_with(|map, head, _| {
4017 (movement::next_word_end(map, head), SelectionGoal::None)
4018 });
4019 })
4020 }
4021
4022 pub fn select_to_next_subword_end(
4023 &mut self,
4024 _: &SelectToNextSubwordEnd,
4025 cx: &mut ViewContext<Self>,
4026 ) {
4027 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4028 s.move_heads_with(|map, head, _| {
4029 (movement::next_subword_end(map, head), SelectionGoal::None)
4030 });
4031 })
4032 }
4033
4034 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4035 self.transact(cx, |this, cx| {
4036 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4037 let line_mode = s.line_mode;
4038 s.move_with(|map, selection| {
4039 if selection.is_empty() && !line_mode {
4040 let cursor = movement::next_word_end(map, selection.head());
4041 selection.set_head(cursor, SelectionGoal::None);
4042 }
4043 });
4044 });
4045 this.insert("", cx);
4046 });
4047 }
4048
4049 pub fn delete_to_next_subword_end(
4050 &mut self,
4051 _: &DeleteToNextSubwordEnd,
4052 cx: &mut ViewContext<Self>,
4053 ) {
4054 self.transact(cx, |this, cx| {
4055 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4056 s.move_with(|map, selection| {
4057 if selection.is_empty() {
4058 let cursor = movement::next_subword_end(map, selection.head());
4059 selection.set_head(cursor, SelectionGoal::None);
4060 }
4061 });
4062 });
4063 this.insert("", cx);
4064 });
4065 }
4066
4067 pub fn move_to_beginning_of_line(
4068 &mut self,
4069 _: &MoveToBeginningOfLine,
4070 cx: &mut ViewContext<Self>,
4071 ) {
4072 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4073 s.move_cursors_with(|map, head, _| {
4074 (
4075 movement::line_beginning(map, head, true),
4076 SelectionGoal::None,
4077 )
4078 });
4079 })
4080 }
4081
4082 pub fn select_to_beginning_of_line(
4083 &mut self,
4084 action: &SelectToBeginningOfLine,
4085 cx: &mut ViewContext<Self>,
4086 ) {
4087 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4088 s.move_heads_with(|map, head, _| {
4089 (
4090 movement::line_beginning(map, head, action.stop_at_soft_wraps),
4091 SelectionGoal::None,
4092 )
4093 });
4094 });
4095 }
4096
4097 pub fn delete_to_beginning_of_line(
4098 &mut self,
4099 _: &DeleteToBeginningOfLine,
4100 cx: &mut ViewContext<Self>,
4101 ) {
4102 self.transact(cx, |this, cx| {
4103 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4104 s.move_with(|_, selection| {
4105 selection.reversed = true;
4106 });
4107 });
4108
4109 this.select_to_beginning_of_line(
4110 &SelectToBeginningOfLine {
4111 stop_at_soft_wraps: false,
4112 },
4113 cx,
4114 );
4115 this.backspace(&Backspace, cx);
4116 });
4117 }
4118
4119 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4120 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4121 s.move_cursors_with(|map, head, _| {
4122 (movement::line_end(map, head, true), SelectionGoal::None)
4123 });
4124 })
4125 }
4126
4127 pub fn select_to_end_of_line(
4128 &mut self,
4129 action: &SelectToEndOfLine,
4130 cx: &mut ViewContext<Self>,
4131 ) {
4132 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4133 s.move_heads_with(|map, head, _| {
4134 (
4135 movement::line_end(map, head, action.stop_at_soft_wraps),
4136 SelectionGoal::None,
4137 )
4138 });
4139 })
4140 }
4141
4142 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4143 self.transact(cx, |this, cx| {
4144 this.select_to_end_of_line(
4145 &SelectToEndOfLine {
4146 stop_at_soft_wraps: false,
4147 },
4148 cx,
4149 );
4150 this.delete(&Delete, cx);
4151 });
4152 }
4153
4154 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4155 self.transact(cx, |this, cx| {
4156 this.select_to_end_of_line(
4157 &SelectToEndOfLine {
4158 stop_at_soft_wraps: false,
4159 },
4160 cx,
4161 );
4162 this.cut(&Cut, cx);
4163 });
4164 }
4165
4166 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4167 if matches!(self.mode, EditorMode::SingleLine) {
4168 cx.propagate_action();
4169 return;
4170 }
4171
4172 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4173 s.select_ranges(vec![0..0]);
4174 });
4175 }
4176
4177 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4178 let mut selection = self.selections.last::<Point>(cx);
4179 selection.set_head(Point::zero(), SelectionGoal::None);
4180
4181 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4182 s.select(vec![selection]);
4183 });
4184 }
4185
4186 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4187 if matches!(self.mode, EditorMode::SingleLine) {
4188 cx.propagate_action();
4189 return;
4190 }
4191
4192 let cursor = self.buffer.read(cx).read(cx).len();
4193 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4194 s.select_ranges(vec![cursor..cursor])
4195 });
4196 }
4197
4198 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4199 self.nav_history = nav_history;
4200 }
4201
4202 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4203 self.nav_history.as_ref()
4204 }
4205
4206 fn push_to_nav_history(
4207 &self,
4208 position: Anchor,
4209 new_position: Option<Point>,
4210 cx: &mut ViewContext<Self>,
4211 ) {
4212 if let Some(nav_history) = &self.nav_history {
4213 let buffer = self.buffer.read(cx).read(cx);
4214 let point = position.to_point(&buffer);
4215 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4216 drop(buffer);
4217
4218 if let Some(new_position) = new_position {
4219 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4220 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4221 return;
4222 }
4223 }
4224
4225 nav_history.push(
4226 Some(NavigationData {
4227 cursor_anchor: position,
4228 cursor_position: point,
4229 scroll_position: self.scroll_position,
4230 scroll_top_anchor: self.scroll_top_anchor.clone(),
4231 scroll_top_row,
4232 }),
4233 cx,
4234 );
4235 }
4236 }
4237
4238 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4239 let buffer = self.buffer.read(cx).snapshot(cx);
4240 let mut selection = self.selections.first::<usize>(cx);
4241 selection.set_head(buffer.len(), SelectionGoal::None);
4242 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4243 s.select(vec![selection]);
4244 });
4245 }
4246
4247 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4248 let end = self.buffer.read(cx).read(cx).len();
4249 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4250 s.select_ranges(vec![0..end]);
4251 });
4252 }
4253
4254 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4255 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4256 let mut selections = self.selections.all::<Point>(cx);
4257 let max_point = display_map.buffer_snapshot.max_point();
4258 for selection in &mut selections {
4259 let rows = selection.spanned_rows(true, &display_map);
4260 selection.start = Point::new(rows.start, 0);
4261 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4262 selection.reversed = false;
4263 }
4264 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4265 s.select(selections);
4266 });
4267 }
4268
4269 pub fn split_selection_into_lines(
4270 &mut self,
4271 _: &SplitSelectionIntoLines,
4272 cx: &mut ViewContext<Self>,
4273 ) {
4274 let mut to_unfold = Vec::new();
4275 let mut new_selection_ranges = Vec::new();
4276 {
4277 let selections = self.selections.all::<Point>(cx);
4278 let buffer = self.buffer.read(cx).read(cx);
4279 for selection in selections {
4280 for row in selection.start.row..selection.end.row {
4281 let cursor = Point::new(row, buffer.line_len(row));
4282 new_selection_ranges.push(cursor..cursor);
4283 }
4284 new_selection_ranges.push(selection.end..selection.end);
4285 to_unfold.push(selection.start..selection.end);
4286 }
4287 }
4288 self.unfold_ranges(to_unfold, true, cx);
4289 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4290 s.select_ranges(new_selection_ranges);
4291 });
4292 }
4293
4294 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4295 self.add_selection(true, cx);
4296 }
4297
4298 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4299 self.add_selection(false, cx);
4300 }
4301
4302 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4303 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4304 let mut selections = self.selections.all::<Point>(cx);
4305 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4306 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4307 let range = oldest_selection.display_range(&display_map).sorted();
4308 let columns = cmp::min(range.start.column(), range.end.column())
4309 ..cmp::max(range.start.column(), range.end.column());
4310
4311 selections.clear();
4312 let mut stack = Vec::new();
4313 for row in range.start.row()..=range.end.row() {
4314 if let Some(selection) = self.selections.build_columnar_selection(
4315 &display_map,
4316 row,
4317 &columns,
4318 oldest_selection.reversed,
4319 ) {
4320 stack.push(selection.id);
4321 selections.push(selection);
4322 }
4323 }
4324
4325 if above {
4326 stack.reverse();
4327 }
4328
4329 AddSelectionsState { above, stack }
4330 });
4331
4332 let last_added_selection = *state.stack.last().unwrap();
4333 let mut new_selections = Vec::new();
4334 if above == state.above {
4335 let end_row = if above {
4336 0
4337 } else {
4338 display_map.max_point().row()
4339 };
4340
4341 'outer: for selection in selections {
4342 if selection.id == last_added_selection {
4343 let range = selection.display_range(&display_map).sorted();
4344 debug_assert_eq!(range.start.row(), range.end.row());
4345 let mut row = range.start.row();
4346 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4347 {
4348 start..end
4349 } else {
4350 cmp::min(range.start.column(), range.end.column())
4351 ..cmp::max(range.start.column(), range.end.column())
4352 };
4353
4354 while row != end_row {
4355 if above {
4356 row -= 1;
4357 } else {
4358 row += 1;
4359 }
4360
4361 if let Some(new_selection) = self.selections.build_columnar_selection(
4362 &display_map,
4363 row,
4364 &columns,
4365 selection.reversed,
4366 ) {
4367 state.stack.push(new_selection.id);
4368 if above {
4369 new_selections.push(new_selection);
4370 new_selections.push(selection);
4371 } else {
4372 new_selections.push(selection);
4373 new_selections.push(new_selection);
4374 }
4375
4376 continue 'outer;
4377 }
4378 }
4379 }
4380
4381 new_selections.push(selection);
4382 }
4383 } else {
4384 new_selections = selections;
4385 new_selections.retain(|s| s.id != last_added_selection);
4386 state.stack.pop();
4387 }
4388
4389 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4390 s.select(new_selections);
4391 });
4392 if state.stack.len() > 1 {
4393 self.add_selections_state = Some(state);
4394 }
4395 }
4396
4397 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4398 self.push_to_selection_history();
4399 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4400 let buffer = &display_map.buffer_snapshot;
4401 let mut selections = self.selections.all::<usize>(cx);
4402 if let Some(mut select_next_state) = self.select_next_state.take() {
4403 let query = &select_next_state.query;
4404 if !select_next_state.done {
4405 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4406 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4407 let mut next_selected_range = None;
4408
4409 let bytes_after_last_selection =
4410 buffer.bytes_in_range(last_selection.end..buffer.len());
4411 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4412 let query_matches = query
4413 .stream_find_iter(bytes_after_last_selection)
4414 .map(|result| (last_selection.end, result))
4415 .chain(
4416 query
4417 .stream_find_iter(bytes_before_first_selection)
4418 .map(|result| (0, result)),
4419 );
4420 for (start_offset, query_match) in query_matches {
4421 let query_match = query_match.unwrap(); // can only fail due to I/O
4422 let offset_range =
4423 start_offset + query_match.start()..start_offset + query_match.end();
4424 let display_range = offset_range.start.to_display_point(&display_map)
4425 ..offset_range.end.to_display_point(&display_map);
4426
4427 if !select_next_state.wordwise
4428 || (!movement::is_inside_word(&display_map, display_range.start)
4429 && !movement::is_inside_word(&display_map, display_range.end))
4430 {
4431 next_selected_range = Some(offset_range);
4432 break;
4433 }
4434 }
4435
4436 if let Some(next_selected_range) = next_selected_range {
4437 self.unfold_ranges([next_selected_range.clone()], false, cx);
4438 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4439 if action.replace_newest {
4440 s.delete(s.newest_anchor().id);
4441 }
4442 s.insert_range(next_selected_range);
4443 });
4444 } else {
4445 select_next_state.done = true;
4446 }
4447 }
4448
4449 self.select_next_state = Some(select_next_state);
4450 } else if selections.len() == 1 {
4451 let selection = selections.last_mut().unwrap();
4452 if selection.start == selection.end {
4453 let word_range = movement::surrounding_word(
4454 &display_map,
4455 selection.start.to_display_point(&display_map),
4456 );
4457 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4458 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4459 selection.goal = SelectionGoal::None;
4460 selection.reversed = false;
4461
4462 let query = buffer
4463 .text_for_range(selection.start..selection.end)
4464 .collect::<String>();
4465 let select_state = SelectNextState {
4466 query: AhoCorasick::new_auto_configured(&[query]),
4467 wordwise: true,
4468 done: false,
4469 };
4470 self.unfold_ranges([selection.start..selection.end], false, cx);
4471 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4472 s.select(selections);
4473 });
4474 self.select_next_state = Some(select_state);
4475 } else {
4476 let query = buffer
4477 .text_for_range(selection.start..selection.end)
4478 .collect::<String>();
4479 self.select_next_state = Some(SelectNextState {
4480 query: AhoCorasick::new_auto_configured(&[query]),
4481 wordwise: false,
4482 done: false,
4483 });
4484 self.select_next(action, cx);
4485 }
4486 }
4487 }
4488
4489 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4490 self.transact(cx, |this, cx| {
4491 let mut selections = this.selections.all::<Point>(cx);
4492 let mut edits = Vec::new();
4493 let mut selection_edit_ranges = Vec::new();
4494 let mut last_toggled_row = None;
4495 let snapshot = this.buffer.read(cx).read(cx);
4496 let empty_str: Arc<str> = "".into();
4497 let mut suffixes_inserted = Vec::new();
4498
4499 fn comment_prefix_range(
4500 snapshot: &MultiBufferSnapshot,
4501 row: u32,
4502 comment_prefix: &str,
4503 comment_prefix_whitespace: &str,
4504 ) -> Range<Point> {
4505 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4506
4507 let mut line_bytes = snapshot
4508 .bytes_in_range(start..snapshot.max_point())
4509 .flatten()
4510 .copied();
4511
4512 // If this line currently begins with the line comment prefix, then record
4513 // the range containing the prefix.
4514 if line_bytes
4515 .by_ref()
4516 .take(comment_prefix.len())
4517 .eq(comment_prefix.bytes())
4518 {
4519 // Include any whitespace that matches the comment prefix.
4520 let matching_whitespace_len = line_bytes
4521 .zip(comment_prefix_whitespace.bytes())
4522 .take_while(|(a, b)| a == b)
4523 .count() as u32;
4524 let end = Point::new(
4525 start.row,
4526 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4527 );
4528 start..end
4529 } else {
4530 start..start
4531 }
4532 }
4533
4534 fn comment_suffix_range(
4535 snapshot: &MultiBufferSnapshot,
4536 row: u32,
4537 comment_suffix: &str,
4538 comment_suffix_has_leading_space: bool,
4539 ) -> Range<Point> {
4540 let end = Point::new(row, snapshot.line_len(row));
4541 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4542
4543 let mut line_end_bytes = snapshot
4544 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4545 .flatten()
4546 .copied();
4547
4548 let leading_space_len = if suffix_start_column > 0
4549 && line_end_bytes.next() == Some(b' ')
4550 && comment_suffix_has_leading_space
4551 {
4552 1
4553 } else {
4554 0
4555 };
4556
4557 // If this line currently begins with the line comment prefix, then record
4558 // the range containing the prefix.
4559 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4560 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4561 start..end
4562 } else {
4563 end..end
4564 }
4565 }
4566
4567 // TODO: Handle selections that cross excerpts
4568 for selection in &mut selections {
4569 let language = if let Some(language) = snapshot.language_at(selection.start) {
4570 language
4571 } else {
4572 continue;
4573 };
4574
4575 selection_edit_ranges.clear();
4576
4577 // If multiple selections contain a given row, avoid processing that
4578 // row more than once.
4579 let mut start_row = selection.start.row;
4580 if last_toggled_row == Some(start_row) {
4581 start_row += 1;
4582 }
4583 let end_row =
4584 if selection.end.row > selection.start.row && selection.end.column == 0 {
4585 selection.end.row - 1
4586 } else {
4587 selection.end.row
4588 };
4589 last_toggled_row = Some(end_row);
4590
4591 if start_row > end_row {
4592 continue;
4593 }
4594
4595 // If the language has line comments, toggle those.
4596 if let Some(full_comment_prefix) = language.line_comment_prefix() {
4597 // Split the comment prefix's trailing whitespace into a separate string,
4598 // as that portion won't be used for detecting if a line is a comment.
4599 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4600 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4601 let mut all_selection_lines_are_comments = true;
4602
4603 for row in start_row..=end_row {
4604 if snapshot.is_line_blank(row) {
4605 continue;
4606 }
4607
4608 let prefix_range = comment_prefix_range(
4609 snapshot.deref(),
4610 row,
4611 comment_prefix,
4612 comment_prefix_whitespace,
4613 );
4614 if prefix_range.is_empty() {
4615 all_selection_lines_are_comments = false;
4616 }
4617 selection_edit_ranges.push(prefix_range);
4618 }
4619
4620 if all_selection_lines_are_comments {
4621 edits.extend(
4622 selection_edit_ranges
4623 .iter()
4624 .cloned()
4625 .map(|range| (range, empty_str.clone())),
4626 );
4627 } else {
4628 let min_column = selection_edit_ranges
4629 .iter()
4630 .map(|r| r.start.column)
4631 .min()
4632 .unwrap_or(0);
4633 edits.extend(selection_edit_ranges.iter().map(|range| {
4634 let position = Point::new(range.start.row, min_column);
4635 (position..position, full_comment_prefix.clone())
4636 }));
4637 }
4638 } else if let Some((full_comment_prefix, comment_suffix)) =
4639 language.block_comment_delimiters()
4640 {
4641 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4642 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4643 let prefix_range = comment_prefix_range(
4644 snapshot.deref(),
4645 start_row,
4646 comment_prefix,
4647 comment_prefix_whitespace,
4648 );
4649 let suffix_range = comment_suffix_range(
4650 snapshot.deref(),
4651 end_row,
4652 comment_suffix.trim_start_matches(' '),
4653 comment_suffix.starts_with(' '),
4654 );
4655
4656 if prefix_range.is_empty() || suffix_range.is_empty() {
4657 edits.push((
4658 prefix_range.start..prefix_range.start,
4659 full_comment_prefix.clone(),
4660 ));
4661 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
4662 suffixes_inserted.push((end_row, comment_suffix.len()));
4663 } else {
4664 edits.push((prefix_range, empty_str.clone()));
4665 edits.push((suffix_range, empty_str.clone()));
4666 }
4667 } else {
4668 continue;
4669 }
4670 }
4671
4672 drop(snapshot);
4673 this.buffer.update(cx, |buffer, cx| {
4674 buffer.edit(edits, None, cx);
4675 });
4676
4677 // Adjust selections so that they end before any comment suffixes that
4678 // were inserted.
4679 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
4680 let mut selections = this.selections.all::<Point>(cx);
4681 let snapshot = this.buffer.read(cx).read(cx);
4682 for selection in &mut selections {
4683 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
4684 match row.cmp(&selection.end.row) {
4685 Ordering::Less => {
4686 suffixes_inserted.next();
4687 continue;
4688 }
4689 Ordering::Greater => break,
4690 Ordering::Equal => {
4691 if selection.end.column == snapshot.line_len(row) {
4692 if selection.is_empty() {
4693 selection.start.column -= suffix_len as u32;
4694 }
4695 selection.end.column -= suffix_len as u32;
4696 }
4697 break;
4698 }
4699 }
4700 }
4701 }
4702
4703 drop(snapshot);
4704 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4705 });
4706 }
4707
4708 pub fn select_larger_syntax_node(
4709 &mut self,
4710 _: &SelectLargerSyntaxNode,
4711 cx: &mut ViewContext<Self>,
4712 ) {
4713 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4714 let buffer = self.buffer.read(cx).snapshot(cx);
4715 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4716
4717 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4718 let mut selected_larger_node = false;
4719 let new_selections = old_selections
4720 .iter()
4721 .map(|selection| {
4722 let old_range = selection.start..selection.end;
4723 let mut new_range = old_range.clone();
4724 while let Some(containing_range) =
4725 buffer.range_for_syntax_ancestor(new_range.clone())
4726 {
4727 new_range = containing_range;
4728 if !display_map.intersects_fold(new_range.start)
4729 && !display_map.intersects_fold(new_range.end)
4730 {
4731 break;
4732 }
4733 }
4734
4735 selected_larger_node |= new_range != old_range;
4736 Selection {
4737 id: selection.id,
4738 start: new_range.start,
4739 end: new_range.end,
4740 goal: SelectionGoal::None,
4741 reversed: selection.reversed,
4742 }
4743 })
4744 .collect::<Vec<_>>();
4745
4746 if selected_larger_node {
4747 stack.push(old_selections);
4748 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4749 s.select(new_selections);
4750 });
4751 }
4752 self.select_larger_syntax_node_stack = stack;
4753 }
4754
4755 pub fn select_smaller_syntax_node(
4756 &mut self,
4757 _: &SelectSmallerSyntaxNode,
4758 cx: &mut ViewContext<Self>,
4759 ) {
4760 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4761 if let Some(selections) = stack.pop() {
4762 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4763 s.select(selections.to_vec());
4764 });
4765 }
4766 self.select_larger_syntax_node_stack = stack;
4767 }
4768
4769 pub fn move_to_enclosing_bracket(
4770 &mut self,
4771 _: &MoveToEnclosingBracket,
4772 cx: &mut ViewContext<Self>,
4773 ) {
4774 let buffer = self.buffer.read(cx).snapshot(cx);
4775 let mut selections = self.selections.all::<usize>(cx);
4776 for selection in &mut selections {
4777 if let Some((open_range, close_range)) =
4778 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4779 {
4780 let close_range = close_range.to_inclusive();
4781 let destination = if close_range.contains(&selection.start)
4782 && close_range.contains(&selection.end)
4783 {
4784 open_range.end
4785 } else {
4786 *close_range.start()
4787 };
4788 selection.start = destination;
4789 selection.end = destination;
4790 }
4791 }
4792
4793 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4794 s.select(selections);
4795 });
4796 }
4797
4798 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4799 self.end_selection(cx);
4800 self.selection_history.mode = SelectionHistoryMode::Undoing;
4801 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4802 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4803 self.select_next_state = entry.select_next_state;
4804 self.add_selections_state = entry.add_selections_state;
4805 self.request_autoscroll(Autoscroll::Newest, cx);
4806 }
4807 self.selection_history.mode = SelectionHistoryMode::Normal;
4808 }
4809
4810 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4811 self.end_selection(cx);
4812 self.selection_history.mode = SelectionHistoryMode::Redoing;
4813 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4814 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4815 self.select_next_state = entry.select_next_state;
4816 self.add_selections_state = entry.add_selections_state;
4817 self.request_autoscroll(Autoscroll::Newest, cx);
4818 }
4819 self.selection_history.mode = SelectionHistoryMode::Normal;
4820 }
4821
4822 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
4823 self.go_to_diagnostic_impl(Direction::Next, cx)
4824 }
4825
4826 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4827 self.go_to_diagnostic_impl(Direction::Prev, cx)
4828 }
4829
4830 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4831 let buffer = self.buffer.read(cx).snapshot(cx);
4832 let selection = self.selections.newest::<usize>(cx);
4833
4834 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
4835 if direction == Direction::Next {
4836 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
4837 let (group_id, jump_to) = popover.activation_info();
4838 if self.activate_diagnostics(group_id, cx) {
4839 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4840 let mut new_selection = s.newest_anchor().clone();
4841 new_selection.collapse_to(jump_to, SelectionGoal::None);
4842 s.select_anchors(vec![new_selection.clone()]);
4843 });
4844 }
4845 return;
4846 }
4847 }
4848
4849 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4850 active_diagnostics
4851 .primary_range
4852 .to_offset(&buffer)
4853 .to_inclusive()
4854 });
4855 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4856 if active_primary_range.contains(&selection.head()) {
4857 *active_primary_range.end()
4858 } else {
4859 selection.head()
4860 }
4861 } else {
4862 selection.head()
4863 };
4864
4865 loop {
4866 let mut diagnostics = if direction == Direction::Prev {
4867 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4868 } else {
4869 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4870 };
4871 let group = diagnostics.find_map(|entry| {
4872 if entry.diagnostic.is_primary
4873 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4874 && !entry.range.is_empty()
4875 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4876 {
4877 Some((entry.range, entry.diagnostic.group_id))
4878 } else {
4879 None
4880 }
4881 });
4882
4883 if let Some((primary_range, group_id)) = group {
4884 if self.activate_diagnostics(group_id, cx) {
4885 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4886 s.select(vec![Selection {
4887 id: selection.id,
4888 start: primary_range.start,
4889 end: primary_range.start,
4890 reversed: false,
4891 goal: SelectionGoal::None,
4892 }]);
4893 });
4894 }
4895 break;
4896 } else {
4897 // Cycle around to the start of the buffer, potentially moving back to the start of
4898 // the currently active diagnostic.
4899 active_primary_range.take();
4900 if direction == Direction::Prev {
4901 if search_start == buffer.len() {
4902 break;
4903 } else {
4904 search_start = buffer.len();
4905 }
4906 } else if search_start == 0 {
4907 break;
4908 } else {
4909 search_start = 0;
4910 }
4911 }
4912 }
4913 }
4914
4915 pub fn go_to_definition(
4916 workspace: &mut Workspace,
4917 _: &GoToDefinition,
4918 cx: &mut ViewContext<Workspace>,
4919 ) {
4920 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
4921 }
4922
4923 pub fn go_to_type_definition(
4924 workspace: &mut Workspace,
4925 _: &GoToTypeDefinition,
4926 cx: &mut ViewContext<Workspace>,
4927 ) {
4928 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
4929 }
4930
4931 fn go_to_definition_of_kind(
4932 kind: GotoDefinitionKind,
4933 workspace: &mut Workspace,
4934 cx: &mut ViewContext<Workspace>,
4935 ) {
4936 let active_item = workspace.active_item(cx);
4937 let editor_handle = if let Some(editor) = active_item
4938 .as_ref()
4939 .and_then(|item| item.act_as::<Self>(cx))
4940 {
4941 editor
4942 } else {
4943 return;
4944 };
4945
4946 let editor = editor_handle.read(cx);
4947 let buffer = editor.buffer.read(cx);
4948 let head = editor.selections.newest::<usize>(cx).head();
4949 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4950 text_anchor
4951 } else {
4952 return;
4953 };
4954
4955 let project = workspace.project().clone();
4956 let definitions = project.update(cx, |project, cx| match kind {
4957 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
4958 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
4959 });
4960
4961 cx.spawn(|workspace, mut cx| async move {
4962 let definitions = definitions.await?;
4963 workspace.update(&mut cx, |workspace, cx| {
4964 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4965 });
4966
4967 Ok::<(), anyhow::Error>(())
4968 })
4969 .detach_and_log_err(cx);
4970 }
4971
4972 pub fn navigate_to_definitions(
4973 workspace: &mut Workspace,
4974 editor_handle: ViewHandle<Editor>,
4975 definitions: Vec<LocationLink>,
4976 cx: &mut ViewContext<Workspace>,
4977 ) {
4978 let pane = workspace.active_pane().clone();
4979 for definition in definitions {
4980 let range = definition
4981 .target
4982 .range
4983 .to_offset(definition.target.buffer.read(cx));
4984
4985 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4986 target_editor_handle.update(cx, |target_editor, cx| {
4987 // When selecting a definition in a different buffer, disable the nav history
4988 // to avoid creating a history entry at the previous cursor location.
4989 if editor_handle != target_editor_handle {
4990 pane.update(cx, |pane, _| pane.disable_history());
4991 }
4992 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4993 s.select_ranges([range]);
4994 });
4995
4996 pane.update(cx, |pane, _| pane.enable_history());
4997 });
4998 }
4999 }
5000
5001 pub fn find_all_references(
5002 workspace: &mut Workspace,
5003 _: &FindAllReferences,
5004 cx: &mut ViewContext<Workspace>,
5005 ) -> Option<Task<Result<()>>> {
5006 let active_item = workspace.active_item(cx)?;
5007 let editor_handle = active_item.act_as::<Self>(cx)?;
5008
5009 let editor = editor_handle.read(cx);
5010 let buffer = editor.buffer.read(cx);
5011 let head = editor.selections.newest::<usize>(cx).head();
5012 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5013 let replica_id = editor.replica_id(cx);
5014
5015 let project = workspace.project().clone();
5016 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5017 Some(cx.spawn(|workspace, mut cx| async move {
5018 let mut locations = references.await?;
5019 if locations.is_empty() {
5020 return Ok(());
5021 }
5022
5023 locations.sort_by_key(|location| location.buffer.id());
5024 let mut locations = locations.into_iter().peekable();
5025 let mut ranges_to_highlight = Vec::new();
5026
5027 let excerpt_buffer = cx.add_model(|cx| {
5028 let mut symbol_name = None;
5029 let mut multibuffer = MultiBuffer::new(replica_id);
5030 while let Some(location) = locations.next() {
5031 let buffer = location.buffer.read(cx);
5032 let mut ranges_for_buffer = Vec::new();
5033 let range = location.range.to_offset(buffer);
5034 ranges_for_buffer.push(range.clone());
5035 if symbol_name.is_none() {
5036 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
5037 }
5038
5039 while let Some(next_location) = locations.peek() {
5040 if next_location.buffer == location.buffer {
5041 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5042 locations.next();
5043 } else {
5044 break;
5045 }
5046 }
5047
5048 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5049 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5050 location.buffer.clone(),
5051 ranges_for_buffer,
5052 1,
5053 cx,
5054 ));
5055 }
5056 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
5057 });
5058
5059 workspace.update(&mut cx, |workspace, cx| {
5060 let editor =
5061 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
5062 editor.update(cx, |editor, cx| {
5063 editor.highlight_background::<Self>(
5064 ranges_to_highlight,
5065 |theme| theme.editor.highlighted_line_background,
5066 cx,
5067 );
5068 });
5069 workspace.add_item(Box::new(editor), cx);
5070 });
5071
5072 Ok(())
5073 }))
5074 }
5075
5076 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5077 use language::ToOffset as _;
5078
5079 let project = self.project.clone()?;
5080 let selection = self.selections.newest_anchor().clone();
5081 let (cursor_buffer, cursor_buffer_position) = self
5082 .buffer
5083 .read(cx)
5084 .text_anchor_for_position(selection.head(), cx)?;
5085 let (tail_buffer, _) = self
5086 .buffer
5087 .read(cx)
5088 .text_anchor_for_position(selection.tail(), cx)?;
5089 if tail_buffer != cursor_buffer {
5090 return None;
5091 }
5092
5093 let snapshot = cursor_buffer.read(cx).snapshot();
5094 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5095 let prepare_rename = project.update(cx, |project, cx| {
5096 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5097 });
5098
5099 Some(cx.spawn(|this, mut cx| async move {
5100 let rename_range = if let Some(range) = prepare_rename.await? {
5101 Some(range)
5102 } else {
5103 this.read_with(&cx, |this, cx| {
5104 let buffer = this.buffer.read(cx).snapshot(cx);
5105 let mut buffer_highlights = this
5106 .document_highlights_for_position(selection.head(), &buffer)
5107 .filter(|highlight| {
5108 highlight.start.excerpt_id() == selection.head().excerpt_id()
5109 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5110 });
5111 buffer_highlights
5112 .next()
5113 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5114 })
5115 };
5116 if let Some(rename_range) = rename_range {
5117 let rename_buffer_range = rename_range.to_offset(&snapshot);
5118 let cursor_offset_in_rename_range =
5119 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5120
5121 this.update(&mut cx, |this, cx| {
5122 this.take_rename(false, cx);
5123 let style = this.style(cx);
5124 let buffer = this.buffer.read(cx).read(cx);
5125 let cursor_offset = selection.head().to_offset(&buffer);
5126 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5127 let rename_end = rename_start + rename_buffer_range.len();
5128 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5129 let mut old_highlight_id = None;
5130 let old_name: Arc<str> = buffer
5131 .chunks(rename_start..rename_end, true)
5132 .map(|chunk| {
5133 if old_highlight_id.is_none() {
5134 old_highlight_id = chunk.syntax_highlight_id;
5135 }
5136 chunk.text
5137 })
5138 .collect::<String>()
5139 .into();
5140
5141 drop(buffer);
5142
5143 // Position the selection in the rename editor so that it matches the current selection.
5144 this.show_local_selections = false;
5145 let rename_editor = cx.add_view(|cx| {
5146 let mut editor = Editor::single_line(None, cx);
5147 if let Some(old_highlight_id) = old_highlight_id {
5148 editor.override_text_style =
5149 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5150 }
5151 editor.buffer.update(cx, |buffer, cx| {
5152 buffer.edit([(0..0, old_name.clone())], None, cx)
5153 });
5154 editor.select_all(&SelectAll, cx);
5155 editor
5156 });
5157
5158 let ranges = this
5159 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5160 .into_iter()
5161 .flat_map(|(_, ranges)| ranges)
5162 .chain(
5163 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5164 .into_iter()
5165 .flat_map(|(_, ranges)| ranges),
5166 )
5167 .collect();
5168
5169 this.highlight_text::<Rename>(
5170 ranges,
5171 HighlightStyle {
5172 fade_out: Some(style.rename_fade),
5173 ..Default::default()
5174 },
5175 cx,
5176 );
5177 cx.focus(&rename_editor);
5178 let block_id = this.insert_blocks(
5179 [BlockProperties {
5180 style: BlockStyle::Flex,
5181 position: range.start.clone(),
5182 height: 1,
5183 render: Arc::new({
5184 let editor = rename_editor.clone();
5185 move |cx: &mut BlockContext| {
5186 ChildView::new(editor.clone())
5187 .contained()
5188 .with_padding_left(cx.anchor_x)
5189 .boxed()
5190 }
5191 }),
5192 disposition: BlockDisposition::Below,
5193 }],
5194 cx,
5195 )[0];
5196 this.pending_rename = Some(RenameState {
5197 range,
5198 old_name,
5199 editor: rename_editor,
5200 block_id,
5201 });
5202 });
5203 }
5204
5205 Ok(())
5206 }))
5207 }
5208
5209 pub fn confirm_rename(
5210 workspace: &mut Workspace,
5211 _: &ConfirmRename,
5212 cx: &mut ViewContext<Workspace>,
5213 ) -> Option<Task<Result<()>>> {
5214 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5215
5216 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5217 let rename = editor.take_rename(false, cx)?;
5218 let buffer = editor.buffer.read(cx);
5219 let (start_buffer, start) =
5220 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5221 let (end_buffer, end) =
5222 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5223 if start_buffer == end_buffer {
5224 let new_name = rename.editor.read(cx).text(cx);
5225 Some((start_buffer, start..end, rename.old_name, new_name))
5226 } else {
5227 None
5228 }
5229 })?;
5230
5231 let rename = workspace.project().clone().update(cx, |project, cx| {
5232 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5233 });
5234
5235 Some(cx.spawn(|workspace, mut cx| async move {
5236 let project_transaction = rename.await?;
5237 Self::open_project_transaction(
5238 editor.clone(),
5239 workspace,
5240 project_transaction,
5241 format!("Rename: {} → {}", old_name, new_name),
5242 cx.clone(),
5243 )
5244 .await?;
5245
5246 editor.update(&mut cx, |editor, cx| {
5247 editor.refresh_document_highlights(cx);
5248 });
5249 Ok(())
5250 }))
5251 }
5252
5253 fn take_rename(
5254 &mut self,
5255 moving_cursor: bool,
5256 cx: &mut ViewContext<Self>,
5257 ) -> Option<RenameState> {
5258 let rename = self.pending_rename.take()?;
5259 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5260 self.clear_text_highlights::<Rename>(cx);
5261 self.show_local_selections = true;
5262
5263 if moving_cursor {
5264 let rename_editor = rename.editor.read(cx);
5265 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5266
5267 // Update the selection to match the position of the selection inside
5268 // the rename editor.
5269 let snapshot = self.buffer.read(cx).read(cx);
5270 let rename_range = rename.range.to_offset(&snapshot);
5271 let cursor_in_editor = snapshot
5272 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5273 .min(rename_range.end);
5274 drop(snapshot);
5275
5276 self.change_selections(None, cx, |s| {
5277 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5278 });
5279 } else {
5280 self.refresh_document_highlights(cx);
5281 }
5282
5283 Some(rename)
5284 }
5285
5286 #[cfg(any(test, feature = "test-support"))]
5287 pub fn pending_rename(&self) -> Option<&RenameState> {
5288 self.pending_rename.as_ref()
5289 }
5290
5291 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5292 let project = match &self.project {
5293 Some(project) => project.clone(),
5294 None => return None,
5295 };
5296
5297 Some(self.perform_format(project, cx))
5298 }
5299
5300 fn perform_format(
5301 &mut self,
5302 project: ModelHandle<Project>,
5303 cx: &mut ViewContext<'_, Self>,
5304 ) -> Task<Result<()>> {
5305 let buffer = self.buffer().clone();
5306 let buffers = buffer.read(cx).all_buffers();
5307
5308 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5309 let format = project.update(cx, |project, cx| {
5310 project.format(buffers, true, FormatTrigger::Manual, cx)
5311 });
5312
5313 cx.spawn(|_, mut cx| async move {
5314 let transaction = futures::select_biased! {
5315 _ = timeout => {
5316 log::warn!("timed out waiting for formatting");
5317 None
5318 }
5319 transaction = format.log_err().fuse() => transaction,
5320 };
5321
5322 buffer.update(&mut cx, |buffer, cx| {
5323 if let Some(transaction) = transaction {
5324 if !buffer.is_singleton() {
5325 buffer.push_transaction(&transaction.0);
5326 }
5327 }
5328
5329 cx.notify();
5330 });
5331
5332 Ok(())
5333 })
5334 }
5335
5336 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5337 if let Some(project) = self.project.clone() {
5338 self.buffer.update(cx, |multi_buffer, cx| {
5339 project.update(cx, |project, cx| {
5340 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5341 });
5342 })
5343 }
5344 }
5345
5346 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5347 cx.show_character_palette();
5348 }
5349
5350 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5351 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5352 let buffer = self.buffer.read(cx).snapshot(cx);
5353 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5354 let is_valid = buffer
5355 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5356 .any(|entry| {
5357 entry.diagnostic.is_primary
5358 && !entry.range.is_empty()
5359 && entry.range.start == primary_range_start
5360 && entry.diagnostic.message == active_diagnostics.primary_message
5361 });
5362
5363 if is_valid != active_diagnostics.is_valid {
5364 active_diagnostics.is_valid = is_valid;
5365 let mut new_styles = HashMap::default();
5366 for (block_id, diagnostic) in &active_diagnostics.blocks {
5367 new_styles.insert(
5368 *block_id,
5369 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5370 );
5371 }
5372 self.display_map
5373 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5374 }
5375 }
5376 }
5377
5378 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5379 self.dismiss_diagnostics(cx);
5380 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5381 let buffer = self.buffer.read(cx).snapshot(cx);
5382
5383 let mut primary_range = None;
5384 let mut primary_message = None;
5385 let mut group_end = Point::zero();
5386 let diagnostic_group = buffer
5387 .diagnostic_group::<Point>(group_id)
5388 .map(|entry| {
5389 if entry.range.end > group_end {
5390 group_end = entry.range.end;
5391 }
5392 if entry.diagnostic.is_primary {
5393 primary_range = Some(entry.range.clone());
5394 primary_message = Some(entry.diagnostic.message.clone());
5395 }
5396 entry
5397 })
5398 .collect::<Vec<_>>();
5399 let primary_range = primary_range?;
5400 let primary_message = primary_message?;
5401 let primary_range =
5402 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5403
5404 let blocks = display_map
5405 .insert_blocks(
5406 diagnostic_group.iter().map(|entry| {
5407 let diagnostic = entry.diagnostic.clone();
5408 let message_height = diagnostic.message.lines().count() as u8;
5409 BlockProperties {
5410 style: BlockStyle::Fixed,
5411 position: buffer.anchor_after(entry.range.start),
5412 height: message_height,
5413 render: diagnostic_block_renderer(diagnostic, true),
5414 disposition: BlockDisposition::Below,
5415 }
5416 }),
5417 cx,
5418 )
5419 .into_iter()
5420 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5421 .collect();
5422
5423 Some(ActiveDiagnosticGroup {
5424 primary_range,
5425 primary_message,
5426 blocks,
5427 is_valid: true,
5428 })
5429 });
5430 self.active_diagnostics.is_some()
5431 }
5432
5433 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5434 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5435 self.display_map.update(cx, |display_map, cx| {
5436 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5437 });
5438 cx.notify();
5439 }
5440 }
5441
5442 pub fn set_selections_from_remote(
5443 &mut self,
5444 selections: Vec<Selection<Anchor>>,
5445 cx: &mut ViewContext<Self>,
5446 ) {
5447 let old_cursor_position = self.selections.newest_anchor().head();
5448 self.selections.change_with(cx, |s| {
5449 s.select_anchors(selections);
5450 });
5451 self.selections_did_change(false, &old_cursor_position, cx);
5452 }
5453
5454 fn push_to_selection_history(&mut self) {
5455 self.selection_history.push(SelectionHistoryEntry {
5456 selections: self.selections.disjoint_anchors(),
5457 select_next_state: self.select_next_state.clone(),
5458 add_selections_state: self.add_selections_state.clone(),
5459 });
5460 }
5461
5462 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5463 self.autoscroll_request = Some((autoscroll, true));
5464 cx.notify();
5465 }
5466
5467 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5468 self.autoscroll_request = Some((autoscroll, false));
5469 cx.notify();
5470 }
5471
5472 pub fn transact(
5473 &mut self,
5474 cx: &mut ViewContext<Self>,
5475 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5476 ) -> Option<TransactionId> {
5477 self.start_transaction_at(Instant::now(), cx);
5478 update(self, cx);
5479 self.end_transaction_at(Instant::now(), cx)
5480 }
5481
5482 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5483 self.end_selection(cx);
5484 if let Some(tx_id) = self
5485 .buffer
5486 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5487 {
5488 self.selection_history
5489 .insert_transaction(tx_id, self.selections.disjoint_anchors());
5490 }
5491 }
5492
5493 fn end_transaction_at(
5494 &mut self,
5495 now: Instant,
5496 cx: &mut ViewContext<Self>,
5497 ) -> Option<TransactionId> {
5498 if let Some(tx_id) = self
5499 .buffer
5500 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5501 {
5502 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5503 *end_selections = Some(self.selections.disjoint_anchors());
5504 } else {
5505 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5506 }
5507
5508 cx.emit(Event::Edited);
5509 Some(tx_id)
5510 } else {
5511 None
5512 }
5513 }
5514
5515 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5516 log::info!("Editor::page_up");
5517 }
5518
5519 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5520 log::info!("Editor::page_down");
5521 }
5522
5523 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5524 let mut fold_ranges = Vec::new();
5525
5526 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5527 let selections = self.selections.all::<Point>(cx);
5528 for selection in selections {
5529 let range = selection.display_range(&display_map).sorted();
5530 let buffer_start_row = range.start.to_point(&display_map).row;
5531
5532 for row in (0..=range.end.row()).rev() {
5533 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5534 let fold_range = self.foldable_range_for_line(&display_map, row);
5535 if fold_range.end.row >= buffer_start_row {
5536 fold_ranges.push(fold_range);
5537 if row <= range.start.row() {
5538 break;
5539 }
5540 }
5541 }
5542 }
5543 }
5544
5545 self.fold_ranges(fold_ranges, cx);
5546 }
5547
5548 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5549 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5550 let buffer = &display_map.buffer_snapshot;
5551 let selections = self.selections.all::<Point>(cx);
5552 let ranges = selections
5553 .iter()
5554 .map(|s| {
5555 let range = s.display_range(&display_map).sorted();
5556 let mut start = range.start.to_point(&display_map);
5557 let mut end = range.end.to_point(&display_map);
5558 start.column = 0;
5559 end.column = buffer.line_len(end.row);
5560 start..end
5561 })
5562 .collect::<Vec<_>>();
5563 self.unfold_ranges(ranges, true, cx);
5564 }
5565
5566 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5567 let max_point = display_map.max_point();
5568 if display_row >= max_point.row() {
5569 false
5570 } else {
5571 let (start_indent, is_blank) = display_map.line_indent(display_row);
5572 if is_blank {
5573 false
5574 } else {
5575 for display_row in display_row + 1..=max_point.row() {
5576 let (indent, is_blank) = display_map.line_indent(display_row);
5577 if !is_blank {
5578 return indent > start_indent;
5579 }
5580 }
5581 false
5582 }
5583 }
5584 }
5585
5586 fn foldable_range_for_line(
5587 &self,
5588 display_map: &DisplaySnapshot,
5589 start_row: u32,
5590 ) -> Range<Point> {
5591 let max_point = display_map.max_point();
5592
5593 let (start_indent, _) = display_map.line_indent(start_row);
5594 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5595 let mut end = None;
5596 for row in start_row + 1..=max_point.row() {
5597 let (indent, is_blank) = display_map.line_indent(row);
5598 if !is_blank && indent <= start_indent {
5599 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5600 break;
5601 }
5602 }
5603
5604 let end = end.unwrap_or(max_point);
5605 start.to_point(display_map)..end.to_point(display_map)
5606 }
5607
5608 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5609 let selections = self.selections.all::<Point>(cx);
5610 let ranges = selections.into_iter().map(|s| s.start..s.end);
5611 self.fold_ranges(ranges, cx);
5612 }
5613
5614 pub fn fold_ranges<T: ToOffset>(
5615 &mut self,
5616 ranges: impl IntoIterator<Item = Range<T>>,
5617 cx: &mut ViewContext<Self>,
5618 ) {
5619 let mut ranges = ranges.into_iter().peekable();
5620 if ranges.peek().is_some() {
5621 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5622 self.request_autoscroll(Autoscroll::Fit, cx);
5623 cx.notify();
5624 }
5625 }
5626
5627 pub fn unfold_ranges<T: ToOffset>(
5628 &mut self,
5629 ranges: impl IntoIterator<Item = Range<T>>,
5630 inclusive: bool,
5631 cx: &mut ViewContext<Self>,
5632 ) {
5633 let mut ranges = ranges.into_iter().peekable();
5634 if ranges.peek().is_some() {
5635 self.display_map
5636 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5637 self.request_autoscroll(Autoscroll::Fit, cx);
5638 cx.notify();
5639 }
5640 }
5641
5642 pub fn insert_blocks(
5643 &mut self,
5644 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5645 cx: &mut ViewContext<Self>,
5646 ) -> Vec<BlockId> {
5647 let blocks = self
5648 .display_map
5649 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5650 self.request_autoscroll(Autoscroll::Fit, cx);
5651 blocks
5652 }
5653
5654 pub fn replace_blocks(
5655 &mut self,
5656 blocks: HashMap<BlockId, RenderBlock>,
5657 cx: &mut ViewContext<Self>,
5658 ) {
5659 self.display_map
5660 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5661 self.request_autoscroll(Autoscroll::Fit, cx);
5662 }
5663
5664 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5665 self.display_map.update(cx, |display_map, cx| {
5666 display_map.remove_blocks(block_ids, cx)
5667 });
5668 }
5669
5670 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5671 self.display_map
5672 .update(cx, |map, cx| map.snapshot(cx))
5673 .longest_row()
5674 }
5675
5676 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5677 self.display_map
5678 .update(cx, |map, cx| map.snapshot(cx))
5679 .max_point()
5680 }
5681
5682 pub fn text(&self, cx: &AppContext) -> String {
5683 self.buffer.read(cx).read(cx).text()
5684 }
5685
5686 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5687 self.transact(cx, |this, cx| {
5688 this.buffer
5689 .read(cx)
5690 .as_singleton()
5691 .expect("you can only call set_text on editors for singleton buffers")
5692 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5693 });
5694 }
5695
5696 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5697 self.display_map
5698 .update(cx, |map, cx| map.snapshot(cx))
5699 .text()
5700 }
5701
5702 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5703 let language_name = self
5704 .buffer
5705 .read(cx)
5706 .as_singleton()
5707 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5708 .map(|l| l.name());
5709
5710 let settings = cx.global::<Settings>();
5711 let mode = self
5712 .soft_wrap_mode_override
5713 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5714 match mode {
5715 settings::SoftWrap::None => SoftWrap::None,
5716 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5717 settings::SoftWrap::PreferredLineLength => {
5718 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5719 }
5720 }
5721 }
5722
5723 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5724 self.soft_wrap_mode_override = Some(mode);
5725 cx.notify();
5726 }
5727
5728 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5729 self.display_map
5730 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5731 }
5732
5733 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5734 self.highlighted_rows = rows;
5735 }
5736
5737 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5738 self.highlighted_rows.clone()
5739 }
5740
5741 pub fn highlight_background<T: 'static>(
5742 &mut self,
5743 ranges: Vec<Range<Anchor>>,
5744 color_fetcher: fn(&Theme) -> Color,
5745 cx: &mut ViewContext<Self>,
5746 ) {
5747 self.background_highlights
5748 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5749 cx.notify();
5750 }
5751
5752 #[allow(clippy::type_complexity)]
5753 pub fn clear_background_highlights<T: 'static>(
5754 &mut self,
5755 cx: &mut ViewContext<Self>,
5756 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5757 cx.notify();
5758 self.background_highlights.remove(&TypeId::of::<T>())
5759 }
5760
5761 #[cfg(feature = "test-support")]
5762 pub fn all_background_highlights(
5763 &mut self,
5764 cx: &mut ViewContext<Self>,
5765 ) -> Vec<(Range<DisplayPoint>, Color)> {
5766 let snapshot = self.snapshot(cx);
5767 let buffer = &snapshot.buffer_snapshot;
5768 let start = buffer.anchor_before(0);
5769 let end = buffer.anchor_after(buffer.len());
5770 let theme = cx.global::<Settings>().theme.as_ref();
5771 self.background_highlights_in_range(start..end, &snapshot, theme)
5772 }
5773
5774 fn document_highlights_for_position<'a>(
5775 &'a self,
5776 position: Anchor,
5777 buffer: &'a MultiBufferSnapshot,
5778 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5779 let read_highlights = self
5780 .background_highlights
5781 .get(&TypeId::of::<DocumentHighlightRead>())
5782 .map(|h| &h.1);
5783 let write_highlights = self
5784 .background_highlights
5785 .get(&TypeId::of::<DocumentHighlightWrite>())
5786 .map(|h| &h.1);
5787 let left_position = position.bias_left(buffer);
5788 let right_position = position.bias_right(buffer);
5789 read_highlights
5790 .into_iter()
5791 .chain(write_highlights)
5792 .flat_map(move |ranges| {
5793 let start_ix = match ranges.binary_search_by(|probe| {
5794 let cmp = probe.end.cmp(&left_position, buffer);
5795 if cmp.is_ge() {
5796 Ordering::Greater
5797 } else {
5798 Ordering::Less
5799 }
5800 }) {
5801 Ok(i) | Err(i) => i,
5802 };
5803
5804 let right_position = right_position.clone();
5805 ranges[start_ix..]
5806 .iter()
5807 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
5808 })
5809 }
5810
5811 pub fn background_highlights_in_range(
5812 &self,
5813 search_range: Range<Anchor>,
5814 display_snapshot: &DisplaySnapshot,
5815 theme: &Theme,
5816 ) -> Vec<(Range<DisplayPoint>, Color)> {
5817 let mut results = Vec::new();
5818 let buffer = &display_snapshot.buffer_snapshot;
5819 for (color_fetcher, ranges) in self.background_highlights.values() {
5820 let color = color_fetcher(theme);
5821 let start_ix = match ranges.binary_search_by(|probe| {
5822 let cmp = probe.end.cmp(&search_range.start, buffer);
5823 if cmp.is_gt() {
5824 Ordering::Greater
5825 } else {
5826 Ordering::Less
5827 }
5828 }) {
5829 Ok(i) | Err(i) => i,
5830 };
5831 for range in &ranges[start_ix..] {
5832 if range.start.cmp(&search_range.end, buffer).is_ge() {
5833 break;
5834 }
5835 let start = range
5836 .start
5837 .to_point(buffer)
5838 .to_display_point(display_snapshot);
5839 let end = range
5840 .end
5841 .to_point(buffer)
5842 .to_display_point(display_snapshot);
5843 results.push((start..end, color))
5844 }
5845 }
5846 results
5847 }
5848
5849 pub fn highlight_text<T: 'static>(
5850 &mut self,
5851 ranges: Vec<Range<Anchor>>,
5852 style: HighlightStyle,
5853 cx: &mut ViewContext<Self>,
5854 ) {
5855 self.display_map.update(cx, |map, _| {
5856 map.highlight_text(TypeId::of::<T>(), ranges, style)
5857 });
5858 cx.notify();
5859 }
5860
5861 pub fn text_highlights<'a, T: 'static>(
5862 &'a self,
5863 cx: &'a AppContext,
5864 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
5865 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
5866 }
5867
5868 pub fn clear_text_highlights<T: 'static>(
5869 &mut self,
5870 cx: &mut ViewContext<Self>,
5871 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5872 cx.notify();
5873 self.display_map
5874 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5875 }
5876
5877 fn next_blink_epoch(&mut self) -> usize {
5878 self.blink_epoch += 1;
5879 self.blink_epoch
5880 }
5881
5882 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5883 if !self.focused {
5884 return;
5885 }
5886
5887 self.show_local_cursors = true;
5888 cx.notify();
5889
5890 let epoch = self.next_blink_epoch();
5891 cx.spawn(|this, mut cx| {
5892 let this = this.downgrade();
5893 async move {
5894 Timer::after(CURSOR_BLINK_INTERVAL).await;
5895 if let Some(this) = this.upgrade(&cx) {
5896 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5897 }
5898 }
5899 })
5900 .detach();
5901 }
5902
5903 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5904 if epoch == self.blink_epoch {
5905 self.blinking_paused = false;
5906 self.blink_cursors(epoch, cx);
5907 }
5908 }
5909
5910 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5911 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5912 self.show_local_cursors = !self.show_local_cursors;
5913 cx.notify();
5914
5915 let epoch = self.next_blink_epoch();
5916 cx.spawn(|this, mut cx| {
5917 let this = this.downgrade();
5918 async move {
5919 Timer::after(CURSOR_BLINK_INTERVAL).await;
5920 if let Some(this) = this.upgrade(&cx) {
5921 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5922 }
5923 }
5924 })
5925 .detach();
5926 }
5927 }
5928
5929 pub fn show_local_cursors(&self) -> bool {
5930 self.show_local_cursors && self.focused
5931 }
5932
5933 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5934 cx.notify();
5935 }
5936
5937 fn on_buffer_event(
5938 &mut self,
5939 _: ModelHandle<MultiBuffer>,
5940 event: &language::Event,
5941 cx: &mut ViewContext<Self>,
5942 ) {
5943 match event {
5944 language::Event::Edited => {
5945 self.refresh_active_diagnostics(cx);
5946 self.refresh_code_actions(cx);
5947 cx.emit(Event::BufferEdited);
5948 }
5949 language::Event::Reparsed => cx.emit(Event::Reparsed),
5950 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5951 language::Event::Saved => cx.emit(Event::Saved),
5952 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5953 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5954 language::Event::Closed => cx.emit(Event::Closed),
5955 language::Event::DiagnosticsUpdated => {
5956 self.refresh_active_diagnostics(cx);
5957 }
5958 _ => {}
5959 }
5960 }
5961
5962 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5963 cx.notify();
5964 }
5965
5966 pub fn set_searchable(&mut self, searchable: bool) {
5967 self.searchable = searchable;
5968 }
5969
5970 pub fn searchable(&self) -> bool {
5971 self.searchable
5972 }
5973
5974 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5975 let active_item = workspace.active_item(cx);
5976 let editor_handle = if let Some(editor) = active_item
5977 .as_ref()
5978 .and_then(|item| item.act_as::<Self>(cx))
5979 {
5980 editor
5981 } else {
5982 cx.propagate_action();
5983 return;
5984 };
5985
5986 let editor = editor_handle.read(cx);
5987 let buffer = editor.buffer.read(cx);
5988 if buffer.is_singleton() {
5989 cx.propagate_action();
5990 return;
5991 }
5992
5993 let mut new_selections_by_buffer = HashMap::default();
5994 for selection in editor.selections.all::<usize>(cx) {
5995 for (buffer, mut range) in
5996 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5997 {
5998 if selection.reversed {
5999 mem::swap(&mut range.start, &mut range.end);
6000 }
6001 new_selections_by_buffer
6002 .entry(buffer)
6003 .or_insert(Vec::new())
6004 .push(range)
6005 }
6006 }
6007
6008 editor_handle.update(cx, |editor, cx| {
6009 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6010 });
6011 let pane = workspace.active_pane().clone();
6012 pane.update(cx, |pane, _| pane.disable_history());
6013
6014 // We defer the pane interaction because we ourselves are a workspace item
6015 // and activating a new item causes the pane to call a method on us reentrantly,
6016 // which panics if we're on the stack.
6017 cx.defer(move |workspace, cx| {
6018 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6019 let editor = workspace.open_project_item::<Self>(buffer, cx);
6020 editor.update(cx, |editor, cx| {
6021 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
6022 s.select_ranges(ranges);
6023 });
6024 });
6025 }
6026
6027 pane.update(cx, |pane, _| pane.enable_history());
6028 });
6029 }
6030
6031 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6032 let editor = workspace.open_path(action.path.clone(), true, cx);
6033 let position = action.position;
6034 let anchor = action.anchor;
6035 cx.spawn_weak(|_, mut cx| async move {
6036 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6037 editor.update(&mut cx, |editor, cx| {
6038 let buffer = editor.buffer().read(cx).as_singleton()?;
6039 let buffer = buffer.read(cx);
6040 let cursor = if buffer.can_resolve(&anchor) {
6041 language::ToPoint::to_point(&anchor, buffer)
6042 } else {
6043 buffer.clip_point(position, Bias::Left)
6044 };
6045
6046 let nav_history = editor.nav_history.take();
6047 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
6048 s.select_ranges([cursor..cursor]);
6049 });
6050 editor.nav_history = nav_history;
6051
6052 Some(())
6053 })?;
6054 Some(())
6055 })
6056 .detach()
6057 }
6058
6059 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6060 let snapshot = self.buffer.read(cx).read(cx);
6061 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6062 Some(
6063 ranges
6064 .iter()
6065 .map(move |range| {
6066 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6067 })
6068 .collect(),
6069 )
6070 }
6071
6072 fn selection_replacement_ranges(
6073 &self,
6074 range: Range<OffsetUtf16>,
6075 cx: &AppContext,
6076 ) -> Vec<Range<OffsetUtf16>> {
6077 let selections = self.selections.all::<OffsetUtf16>(cx);
6078 let newest_selection = selections
6079 .iter()
6080 .max_by_key(|selection| selection.id)
6081 .unwrap();
6082 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6083 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6084 let snapshot = self.buffer.read(cx).read(cx);
6085 selections
6086 .into_iter()
6087 .map(|mut selection| {
6088 selection.start.0 =
6089 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6090 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6091 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6092 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6093 })
6094 .collect()
6095 }
6096
6097 fn report_event(&self, name: &str, cx: &AppContext) {
6098 if let Some((project, file)) = self.project.as_ref().zip(
6099 self.buffer
6100 .read(cx)
6101 .as_singleton()
6102 .and_then(|b| b.read(cx).file()),
6103 ) {
6104 project.read(cx).client().report_event(
6105 name,
6106 json!({
6107 "file_extension": file
6108 .path()
6109 .extension()
6110 .and_then(|e| e.to_str())
6111 }),
6112 );
6113 }
6114 }
6115}
6116
6117impl EditorSnapshot {
6118 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6119 self.display_snapshot.buffer_snapshot.language_at(position)
6120 }
6121
6122 pub fn is_focused(&self) -> bool {
6123 self.is_focused
6124 }
6125
6126 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6127 self.placeholder_text.as_ref()
6128 }
6129
6130 pub fn scroll_position(&self) -> Vector2F {
6131 compute_scroll_position(
6132 &self.display_snapshot,
6133 self.scroll_position,
6134 &self.scroll_top_anchor,
6135 )
6136 }
6137}
6138
6139impl Deref for EditorSnapshot {
6140 type Target = DisplaySnapshot;
6141
6142 fn deref(&self) -> &Self::Target {
6143 &self.display_snapshot
6144 }
6145}
6146
6147fn compute_scroll_position(
6148 snapshot: &DisplaySnapshot,
6149 mut scroll_position: Vector2F,
6150 scroll_top_anchor: &Anchor,
6151) -> Vector2F {
6152 if *scroll_top_anchor != Anchor::min() {
6153 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
6154 scroll_position.set_y(scroll_top + scroll_position.y());
6155 } else {
6156 scroll_position.set_y(0.);
6157 }
6158 scroll_position
6159}
6160
6161#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6162pub enum Event {
6163 BufferEdited,
6164 Edited,
6165 Reparsed,
6166 Blurred,
6167 DirtyChanged,
6168 Saved,
6169 TitleChanged,
6170 SelectionsChanged { local: bool },
6171 ScrollPositionChanged { local: bool },
6172 Closed,
6173 IgnoredInput,
6174}
6175
6176pub struct EditorFocused(pub ViewHandle<Editor>);
6177pub struct EditorBlurred(pub ViewHandle<Editor>);
6178pub struct EditorReleased(pub WeakViewHandle<Editor>);
6179
6180impl Entity for Editor {
6181 type Event = Event;
6182
6183 fn release(&mut self, cx: &mut MutableAppContext) {
6184 cx.emit_global(EditorReleased(self.handle.clone()));
6185 }
6186}
6187
6188impl View for Editor {
6189 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6190 let style = self.style(cx);
6191 let font_changed = self.display_map.update(cx, |map, cx| {
6192 map.set_font(style.text.font_id, style.text.font_size, cx)
6193 });
6194
6195 if font_changed {
6196 let handle = self.handle.clone();
6197 cx.defer(move |cx| {
6198 if let Some(editor) = handle.upgrade(cx) {
6199 editor.update(cx, |editor, cx| {
6200 hide_hover(editor, cx);
6201 hide_link_definition(editor, cx);
6202 })
6203 }
6204 });
6205 }
6206
6207 Stack::new()
6208 .with_child(
6209 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
6210 )
6211 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
6212 .boxed()
6213 }
6214
6215 fn ui_name() -> &'static str {
6216 "Editor"
6217 }
6218
6219 fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6220 let focused_event = EditorFocused(cx.handle());
6221 cx.emit_global(focused_event);
6222 if let Some(rename) = self.pending_rename.as_ref() {
6223 cx.focus(&rename.editor);
6224 } else {
6225 self.focused = true;
6226 self.blink_cursors(self.blink_epoch, cx);
6227 self.buffer.update(cx, |buffer, cx| {
6228 buffer.finalize_last_transaction(cx);
6229 if self.leader_replica_id.is_none() {
6230 buffer.set_active_selections(
6231 &self.selections.disjoint_anchors(),
6232 self.selections.line_mode,
6233 cx,
6234 );
6235 }
6236 });
6237 }
6238 }
6239
6240 fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6241 let blurred_event = EditorBlurred(cx.handle());
6242 cx.emit_global(blurred_event);
6243 self.focused = false;
6244 self.buffer
6245 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6246 self.hide_context_menu(cx);
6247 hide_hover(self, cx);
6248 cx.emit(Event::Blurred);
6249 cx.notify();
6250 }
6251
6252 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6253 let mut context = Self::default_keymap_context();
6254 let mode = match self.mode {
6255 EditorMode::SingleLine => "single_line",
6256 EditorMode::AutoHeight { .. } => "auto_height",
6257 EditorMode::Full => "full",
6258 };
6259 context.map.insert("mode".into(), mode.into());
6260 if self.pending_rename.is_some() {
6261 context.set.insert("renaming".into());
6262 }
6263 match self.context_menu.as_ref() {
6264 Some(ContextMenu::Completions(_)) => {
6265 context.set.insert("showing_completions".into());
6266 }
6267 Some(ContextMenu::CodeActions(_)) => {
6268 context.set.insert("showing_code_actions".into());
6269 }
6270 None => {}
6271 }
6272
6273 for layer in self.keymap_context_layers.values() {
6274 context.extend(layer);
6275 }
6276
6277 context
6278 }
6279
6280 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6281 Some(
6282 self.buffer
6283 .read(cx)
6284 .read(cx)
6285 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6286 .collect(),
6287 )
6288 }
6289
6290 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6291 // Prevent the IME menu from appearing when holding down an alphabetic key
6292 // while input is disabled.
6293 if !self.input_enabled {
6294 return None;
6295 }
6296
6297 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6298 Some(range.start.0..range.end.0)
6299 }
6300
6301 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6302 let snapshot = self.buffer.read(cx).read(cx);
6303 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6304 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6305 }
6306
6307 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6308 self.clear_text_highlights::<InputComposition>(cx);
6309 self.ime_transaction.take();
6310 }
6311
6312 fn replace_text_in_range(
6313 &mut self,
6314 range_utf16: Option<Range<usize>>,
6315 text: &str,
6316 cx: &mut ViewContext<Self>,
6317 ) {
6318 if !self.input_enabled {
6319 cx.emit(Event::IgnoredInput);
6320 return;
6321 }
6322
6323 self.transact(cx, |this, cx| {
6324 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6325 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6326 Some(this.selection_replacement_ranges(range_utf16, cx))
6327 } else {
6328 this.marked_text_ranges(cx)
6329 };
6330
6331 if let Some(new_selected_ranges) = new_selected_ranges {
6332 this.change_selections(None, cx, |selections| {
6333 selections.select_ranges(new_selected_ranges)
6334 });
6335 }
6336 this.handle_input(text, cx);
6337 });
6338
6339 if let Some(transaction) = self.ime_transaction {
6340 self.buffer.update(cx, |buffer, cx| {
6341 buffer.group_until_transaction(transaction, cx);
6342 });
6343 }
6344
6345 self.unmark_text(cx);
6346 }
6347
6348 fn replace_and_mark_text_in_range(
6349 &mut self,
6350 range_utf16: Option<Range<usize>>,
6351 text: &str,
6352 new_selected_range_utf16: Option<Range<usize>>,
6353 cx: &mut ViewContext<Self>,
6354 ) {
6355 if !self.input_enabled {
6356 cx.emit(Event::IgnoredInput);
6357 return;
6358 }
6359
6360 let transaction = self.transact(cx, |this, cx| {
6361 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6362 let snapshot = this.buffer.read(cx).read(cx);
6363 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6364 for marked_range in &mut marked_ranges {
6365 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6366 marked_range.start.0 += relative_range_utf16.start;
6367 marked_range.start =
6368 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6369 marked_range.end =
6370 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6371 }
6372 }
6373 Some(marked_ranges)
6374 } else if let Some(range_utf16) = range_utf16 {
6375 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6376 Some(this.selection_replacement_ranges(range_utf16, cx))
6377 } else {
6378 None
6379 };
6380
6381 if let Some(ranges) = ranges_to_replace {
6382 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6383 }
6384
6385 let marked_ranges = {
6386 let snapshot = this.buffer.read(cx).read(cx);
6387 this.selections
6388 .disjoint_anchors()
6389 .iter()
6390 .map(|selection| {
6391 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6392 })
6393 .collect::<Vec<_>>()
6394 };
6395
6396 if text.is_empty() {
6397 this.unmark_text(cx);
6398 } else {
6399 this.highlight_text::<InputComposition>(
6400 marked_ranges.clone(),
6401 this.style(cx).composition_mark,
6402 cx,
6403 );
6404 }
6405
6406 this.handle_input(text, cx);
6407
6408 if let Some(new_selected_range) = new_selected_range_utf16 {
6409 let snapshot = this.buffer.read(cx).read(cx);
6410 let new_selected_ranges = marked_ranges
6411 .into_iter()
6412 .map(|marked_range| {
6413 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6414 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6415 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6416 snapshot.clip_offset_utf16(new_start, Bias::Left)
6417 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6418 })
6419 .collect::<Vec<_>>();
6420
6421 drop(snapshot);
6422 this.change_selections(None, cx, |selections| {
6423 selections.select_ranges(new_selected_ranges)
6424 });
6425 }
6426 });
6427
6428 self.ime_transaction = self.ime_transaction.or(transaction);
6429 if let Some(transaction) = self.ime_transaction {
6430 self.buffer.update(cx, |buffer, cx| {
6431 buffer.group_until_transaction(transaction, cx);
6432 });
6433 }
6434
6435 if self.text_highlights::<InputComposition>(cx).is_none() {
6436 self.ime_transaction.take();
6437 }
6438 }
6439}
6440
6441fn build_style(
6442 settings: &Settings,
6443 get_field_editor_theme: Option<GetFieldEditorTheme>,
6444 override_text_style: Option<&OverrideTextStyle>,
6445 cx: &AppContext,
6446) -> EditorStyle {
6447 let font_cache = cx.font_cache();
6448
6449 let mut theme = settings.theme.editor.clone();
6450 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6451 let field_editor_theme = get_field_editor_theme(&settings.theme);
6452 theme.text_color = field_editor_theme.text.color;
6453 theme.selection = field_editor_theme.selection;
6454 theme.background = field_editor_theme
6455 .container
6456 .background_color
6457 .unwrap_or_default();
6458 EditorStyle {
6459 text: field_editor_theme.text,
6460 placeholder_text: field_editor_theme.placeholder_text,
6461 theme,
6462 }
6463 } else {
6464 let font_family_id = settings.buffer_font_family;
6465 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6466 let font_properties = Default::default();
6467 let font_id = font_cache
6468 .select_font(font_family_id, &font_properties)
6469 .unwrap();
6470 let font_size = settings.buffer_font_size;
6471 EditorStyle {
6472 text: TextStyle {
6473 color: settings.theme.editor.text_color,
6474 font_family_name,
6475 font_family_id,
6476 font_id,
6477 font_size,
6478 font_properties,
6479 underline: Default::default(),
6480 },
6481 placeholder_text: None,
6482 theme,
6483 }
6484 };
6485
6486 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6487 if let Some(highlighted) = style
6488 .text
6489 .clone()
6490 .highlight(highlight_style, font_cache)
6491 .log_err()
6492 {
6493 style.text = highlighted;
6494 }
6495 }
6496
6497 style
6498}
6499
6500trait SelectionExt {
6501 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6502 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6503 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6504 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6505 -> Range<u32>;
6506}
6507
6508impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6509 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6510 let start = self.start.to_point(buffer);
6511 let end = self.end.to_point(buffer);
6512 if self.reversed {
6513 end..start
6514 } else {
6515 start..end
6516 }
6517 }
6518
6519 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6520 let start = self.start.to_offset(buffer);
6521 let end = self.end.to_offset(buffer);
6522 if self.reversed {
6523 end..start
6524 } else {
6525 start..end
6526 }
6527 }
6528
6529 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6530 let start = self
6531 .start
6532 .to_point(&map.buffer_snapshot)
6533 .to_display_point(map);
6534 let end = self
6535 .end
6536 .to_point(&map.buffer_snapshot)
6537 .to_display_point(map);
6538 if self.reversed {
6539 end..start
6540 } else {
6541 start..end
6542 }
6543 }
6544
6545 fn spanned_rows(
6546 &self,
6547 include_end_if_at_line_start: bool,
6548 map: &DisplaySnapshot,
6549 ) -> Range<u32> {
6550 let start = self.start.to_point(&map.buffer_snapshot);
6551 let mut end = self.end.to_point(&map.buffer_snapshot);
6552 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6553 end.row -= 1;
6554 }
6555
6556 let buffer_start = map.prev_line_boundary(start).0;
6557 let buffer_end = map.next_line_boundary(end).0;
6558 buffer_start.row..buffer_end.row + 1
6559 }
6560}
6561
6562impl<T: InvalidationRegion> InvalidationStack<T> {
6563 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6564 where
6565 S: Clone + ToOffset,
6566 {
6567 while let Some(region) = self.last() {
6568 let all_selections_inside_invalidation_ranges =
6569 if selections.len() == region.ranges().len() {
6570 selections
6571 .iter()
6572 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
6573 .all(|(selection, invalidation_range)| {
6574 let head = selection.head().to_offset(buffer);
6575 invalidation_range.start <= head && invalidation_range.end >= head
6576 })
6577 } else {
6578 false
6579 };
6580
6581 if all_selections_inside_invalidation_ranges {
6582 break;
6583 } else {
6584 self.pop();
6585 }
6586 }
6587 }
6588}
6589
6590impl<T> Default for InvalidationStack<T> {
6591 fn default() -> Self {
6592 Self(Default::default())
6593 }
6594}
6595
6596impl<T> Deref for InvalidationStack<T> {
6597 type Target = Vec<T>;
6598
6599 fn deref(&self) -> &Self::Target {
6600 &self.0
6601 }
6602}
6603
6604impl<T> DerefMut for InvalidationStack<T> {
6605 fn deref_mut(&mut self) -> &mut Self::Target {
6606 &mut self.0
6607 }
6608}
6609
6610impl InvalidationRegion for SnippetState {
6611 fn ranges(&self) -> &[Range<Anchor>] {
6612 &self.ranges[self.active_index]
6613 }
6614}
6615
6616impl Deref for EditorStyle {
6617 type Target = theme::Editor;
6618
6619 fn deref(&self) -> &Self::Target {
6620 &self.theme
6621 }
6622}
6623
6624pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6625 let mut highlighted_lines = Vec::new();
6626 for line in diagnostic.message.lines() {
6627 highlighted_lines.push(highlight_diagnostic_message(line));
6628 }
6629
6630 Arc::new(move |cx: &mut BlockContext| {
6631 let settings = cx.global::<Settings>();
6632 let theme = &settings.theme.editor;
6633 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6634 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6635 Flex::column()
6636 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6637 Label::new(
6638 line.clone(),
6639 style.message.clone().with_font_size(font_size),
6640 )
6641 .with_highlights(highlights.clone())
6642 .contained()
6643 .with_margin_left(cx.anchor_x)
6644 .boxed()
6645 }))
6646 .aligned()
6647 .left()
6648 .boxed()
6649 })
6650}
6651
6652pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6653 let mut message_without_backticks = String::new();
6654 let mut prev_offset = 0;
6655 let mut inside_block = false;
6656 let mut highlights = Vec::new();
6657 for (match_ix, (offset, _)) in message
6658 .match_indices('`')
6659 .chain([(message.len(), "")])
6660 .enumerate()
6661 {
6662 message_without_backticks.push_str(&message[prev_offset..offset]);
6663 if inside_block {
6664 highlights.extend(prev_offset - match_ix..offset - match_ix);
6665 }
6666
6667 inside_block = !inside_block;
6668 prev_offset = offset + 1;
6669 }
6670
6671 (message_without_backticks, highlights)
6672}
6673
6674pub fn diagnostic_style(
6675 severity: DiagnosticSeverity,
6676 valid: bool,
6677 theme: &theme::Editor,
6678) -> DiagnosticStyle {
6679 match (severity, valid) {
6680 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6681 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6682 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6683 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6684 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6685 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6686 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6687 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6688 _ => theme.invalid_hint_diagnostic.clone(),
6689 }
6690}
6691
6692pub fn combine_syntax_and_fuzzy_match_highlights(
6693 text: &str,
6694 default_style: HighlightStyle,
6695 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6696 match_indices: &[usize],
6697) -> Vec<(Range<usize>, HighlightStyle)> {
6698 let mut result = Vec::new();
6699 let mut match_indices = match_indices.iter().copied().peekable();
6700
6701 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6702 {
6703 syntax_highlight.weight = None;
6704
6705 // Add highlights for any fuzzy match characters before the next
6706 // syntax highlight range.
6707 while let Some(&match_index) = match_indices.peek() {
6708 if match_index >= range.start {
6709 break;
6710 }
6711 match_indices.next();
6712 let end_index = char_ix_after(match_index, text);
6713 let mut match_style = default_style;
6714 match_style.weight = Some(fonts::Weight::BOLD);
6715 result.push((match_index..end_index, match_style));
6716 }
6717
6718 if range.start == usize::MAX {
6719 break;
6720 }
6721
6722 // Add highlights for any fuzzy match characters within the
6723 // syntax highlight range.
6724 let mut offset = range.start;
6725 while let Some(&match_index) = match_indices.peek() {
6726 if match_index >= range.end {
6727 break;
6728 }
6729
6730 match_indices.next();
6731 if match_index > offset {
6732 result.push((offset..match_index, syntax_highlight));
6733 }
6734
6735 let mut end_index = char_ix_after(match_index, text);
6736 while let Some(&next_match_index) = match_indices.peek() {
6737 if next_match_index == end_index && next_match_index < range.end {
6738 end_index = char_ix_after(next_match_index, text);
6739 match_indices.next();
6740 } else {
6741 break;
6742 }
6743 }
6744
6745 let mut match_style = syntax_highlight;
6746 match_style.weight = Some(fonts::Weight::BOLD);
6747 result.push((match_index..end_index, match_style));
6748 offset = end_index;
6749 }
6750
6751 if offset < range.end {
6752 result.push((offset..range.end, syntax_highlight));
6753 }
6754 }
6755
6756 fn char_ix_after(ix: usize, text: &str) -> usize {
6757 ix + text[ix..].chars().next().unwrap().len_utf8()
6758 }
6759
6760 result
6761}
6762
6763pub fn styled_runs_for_code_label<'a>(
6764 label: &'a CodeLabel,
6765 syntax_theme: &'a theme::SyntaxTheme,
6766) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6767 let fade_out = HighlightStyle {
6768 fade_out: Some(0.35),
6769 ..Default::default()
6770 };
6771
6772 let mut prev_end = label.filter_range.end;
6773 label
6774 .runs
6775 .iter()
6776 .enumerate()
6777 .flat_map(move |(ix, (range, highlight_id))| {
6778 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6779 style
6780 } else {
6781 return Default::default();
6782 };
6783 let mut muted_style = style;
6784 muted_style.highlight(fade_out);
6785
6786 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6787 if range.start >= label.filter_range.end {
6788 if range.start > prev_end {
6789 runs.push((prev_end..range.start, fade_out));
6790 }
6791 runs.push((range.clone(), muted_style));
6792 } else if range.end <= label.filter_range.end {
6793 runs.push((range.clone(), style));
6794 } else {
6795 runs.push((range.start..label.filter_range.end, style));
6796 runs.push((label.filter_range.end..range.end, muted_style));
6797 }
6798 prev_end = cmp::max(prev_end, range.end);
6799
6800 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6801 runs.push((prev_end..label.text.len(), fade_out));
6802 }
6803
6804 runs
6805 })
6806}
6807
6808#[cfg(test)]
6809mod tests {
6810 use crate::test::{
6811 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6812 EditorTestContext,
6813 };
6814
6815 use super::*;
6816 use futures::StreamExt;
6817 use gpui::{
6818 geometry::rect::RectF,
6819 platform::{WindowBounds, WindowOptions},
6820 };
6821 use indoc::indoc;
6822 use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry};
6823 use project::FakeFs;
6824 use settings::EditorSettings;
6825 use std::{cell::RefCell, rc::Rc, time::Instant};
6826 use text::Point;
6827 use unindent::Unindent;
6828 use util::{
6829 assert_set_eq,
6830 test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
6831 };
6832 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6833
6834 #[gpui::test]
6835 fn test_edit_events(cx: &mut MutableAppContext) {
6836 cx.set_global(Settings::test(cx));
6837 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6838
6839 let events = Rc::new(RefCell::new(Vec::new()));
6840 let (_, editor1) = cx.add_window(Default::default(), {
6841 let events = events.clone();
6842 |cx| {
6843 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6844 if matches!(
6845 event,
6846 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6847 ) {
6848 events.borrow_mut().push(("editor1", *event));
6849 }
6850 })
6851 .detach();
6852 Editor::for_buffer(buffer.clone(), None, cx)
6853 }
6854 });
6855 let (_, editor2) = cx.add_window(Default::default(), {
6856 let events = events.clone();
6857 |cx| {
6858 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6859 if matches!(
6860 event,
6861 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6862 ) {
6863 events.borrow_mut().push(("editor2", *event));
6864 }
6865 })
6866 .detach();
6867 Editor::for_buffer(buffer.clone(), None, cx)
6868 }
6869 });
6870 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6871
6872 // Mutating editor 1 will emit an `Edited` event only for that editor.
6873 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6874 assert_eq!(
6875 mem::take(&mut *events.borrow_mut()),
6876 [
6877 ("editor1", Event::Edited),
6878 ("editor1", Event::BufferEdited),
6879 ("editor2", Event::BufferEdited),
6880 ("editor1", Event::DirtyChanged),
6881 ("editor2", Event::DirtyChanged)
6882 ]
6883 );
6884
6885 // Mutating editor 2 will emit an `Edited` event only for that editor.
6886 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6887 assert_eq!(
6888 mem::take(&mut *events.borrow_mut()),
6889 [
6890 ("editor2", Event::Edited),
6891 ("editor1", Event::BufferEdited),
6892 ("editor2", Event::BufferEdited),
6893 ]
6894 );
6895
6896 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6897 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6898 assert_eq!(
6899 mem::take(&mut *events.borrow_mut()),
6900 [
6901 ("editor1", Event::Edited),
6902 ("editor1", Event::BufferEdited),
6903 ("editor2", Event::BufferEdited),
6904 ("editor1", Event::DirtyChanged),
6905 ("editor2", Event::DirtyChanged),
6906 ]
6907 );
6908
6909 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6910 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6911 assert_eq!(
6912 mem::take(&mut *events.borrow_mut()),
6913 [
6914 ("editor1", Event::Edited),
6915 ("editor1", Event::BufferEdited),
6916 ("editor2", Event::BufferEdited),
6917 ("editor1", Event::DirtyChanged),
6918 ("editor2", Event::DirtyChanged),
6919 ]
6920 );
6921
6922 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6923 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6924 assert_eq!(
6925 mem::take(&mut *events.borrow_mut()),
6926 [
6927 ("editor2", Event::Edited),
6928 ("editor1", Event::BufferEdited),
6929 ("editor2", Event::BufferEdited),
6930 ("editor1", Event::DirtyChanged),
6931 ("editor2", Event::DirtyChanged),
6932 ]
6933 );
6934
6935 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6936 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6937 assert_eq!(
6938 mem::take(&mut *events.borrow_mut()),
6939 [
6940 ("editor2", Event::Edited),
6941 ("editor1", Event::BufferEdited),
6942 ("editor2", Event::BufferEdited),
6943 ("editor1", Event::DirtyChanged),
6944 ("editor2", Event::DirtyChanged),
6945 ]
6946 );
6947
6948 // No event is emitted when the mutation is a no-op.
6949 editor2.update(cx, |editor, cx| {
6950 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6951
6952 editor.backspace(&Backspace, cx);
6953 });
6954 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6955 }
6956
6957 #[gpui::test]
6958 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6959 cx.set_global(Settings::test(cx));
6960 let mut now = Instant::now();
6961 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6962 let group_interval = buffer.read(cx).transaction_group_interval();
6963 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6964 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6965
6966 editor.update(cx, |editor, cx| {
6967 editor.start_transaction_at(now, cx);
6968 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6969
6970 editor.insert("cd", cx);
6971 editor.end_transaction_at(now, cx);
6972 assert_eq!(editor.text(cx), "12cd56");
6973 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6974
6975 editor.start_transaction_at(now, cx);
6976 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6977 editor.insert("e", cx);
6978 editor.end_transaction_at(now, cx);
6979 assert_eq!(editor.text(cx), "12cde6");
6980 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6981
6982 now += group_interval + Duration::from_millis(1);
6983 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6984
6985 // Simulate an edit in another editor
6986 buffer.update(cx, |buffer, cx| {
6987 buffer.start_transaction_at(now, cx);
6988 buffer.edit([(0..1, "a")], None, cx);
6989 buffer.edit([(1..1, "b")], None, cx);
6990 buffer.end_transaction_at(now, cx);
6991 });
6992
6993 assert_eq!(editor.text(cx), "ab2cde6");
6994 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6995
6996 // Last transaction happened past the group interval in a different editor.
6997 // Undo it individually and don't restore selections.
6998 editor.undo(&Undo, cx);
6999 assert_eq!(editor.text(cx), "12cde6");
7000 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
7001
7002 // First two transactions happened within the group interval in this editor.
7003 // Undo them together and restore selections.
7004 editor.undo(&Undo, cx);
7005 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
7006 assert_eq!(editor.text(cx), "123456");
7007 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
7008
7009 // Redo the first two transactions together.
7010 editor.redo(&Redo, cx);
7011 assert_eq!(editor.text(cx), "12cde6");
7012 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
7013
7014 // Redo the last transaction on its own.
7015 editor.redo(&Redo, cx);
7016 assert_eq!(editor.text(cx), "ab2cde6");
7017 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
7018
7019 // Test empty transactions.
7020 editor.start_transaction_at(now, cx);
7021 editor.end_transaction_at(now, cx);
7022 editor.undo(&Undo, cx);
7023 assert_eq!(editor.text(cx), "12cde6");
7024 });
7025 }
7026
7027 #[gpui::test]
7028 fn test_ime_composition(cx: &mut MutableAppContext) {
7029 cx.set_global(Settings::test(cx));
7030 let buffer = cx.add_model(|cx| {
7031 let mut buffer = language::Buffer::new(0, "abcde", cx);
7032 // Ensure automatic grouping doesn't occur.
7033 buffer.set_group_interval(Duration::ZERO);
7034 buffer
7035 });
7036
7037 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7038 cx.add_window(Default::default(), |cx| {
7039 let mut editor = build_editor(buffer.clone(), cx);
7040
7041 // Start a new IME composition.
7042 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
7043 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
7044 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
7045 assert_eq!(editor.text(cx), "äbcde");
7046 assert_eq!(
7047 editor.marked_text_ranges(cx),
7048 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
7049 );
7050
7051 // Finalize IME composition.
7052 editor.replace_text_in_range(None, "ā", cx);
7053 assert_eq!(editor.text(cx), "ābcde");
7054 assert_eq!(editor.marked_text_ranges(cx), None);
7055
7056 // IME composition edits are grouped and are undone/redone at once.
7057 editor.undo(&Default::default(), cx);
7058 assert_eq!(editor.text(cx), "abcde");
7059 assert_eq!(editor.marked_text_ranges(cx), None);
7060 editor.redo(&Default::default(), cx);
7061 assert_eq!(editor.text(cx), "ābcde");
7062 assert_eq!(editor.marked_text_ranges(cx), None);
7063
7064 // Start a new IME composition.
7065 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
7066 assert_eq!(
7067 editor.marked_text_ranges(cx),
7068 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
7069 );
7070
7071 // Undoing during an IME composition cancels it.
7072 editor.undo(&Default::default(), cx);
7073 assert_eq!(editor.text(cx), "ābcde");
7074 assert_eq!(editor.marked_text_ranges(cx), None);
7075
7076 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
7077 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
7078 assert_eq!(editor.text(cx), "ābcdè");
7079 assert_eq!(
7080 editor.marked_text_ranges(cx),
7081 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
7082 );
7083
7084 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
7085 editor.replace_text_in_range(Some(4..999), "ę", cx);
7086 assert_eq!(editor.text(cx), "ābcdę");
7087 assert_eq!(editor.marked_text_ranges(cx), None);
7088
7089 // Start a new IME composition with multiple cursors.
7090 editor.change_selections(None, cx, |s| {
7091 s.select_ranges([
7092 OffsetUtf16(1)..OffsetUtf16(1),
7093 OffsetUtf16(3)..OffsetUtf16(3),
7094 OffsetUtf16(5)..OffsetUtf16(5),
7095 ])
7096 });
7097 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
7098 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
7099 assert_eq!(
7100 editor.marked_text_ranges(cx),
7101 Some(vec![
7102 OffsetUtf16(0)..OffsetUtf16(3),
7103 OffsetUtf16(4)..OffsetUtf16(7),
7104 OffsetUtf16(8)..OffsetUtf16(11)
7105 ])
7106 );
7107
7108 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
7109 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
7110 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
7111 assert_eq!(
7112 editor.marked_text_ranges(cx),
7113 Some(vec![
7114 OffsetUtf16(1)..OffsetUtf16(2),
7115 OffsetUtf16(5)..OffsetUtf16(6),
7116 OffsetUtf16(9)..OffsetUtf16(10)
7117 ])
7118 );
7119
7120 // Finalize IME composition with multiple cursors.
7121 editor.replace_text_in_range(Some(9..10), "2", cx);
7122 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
7123 assert_eq!(editor.marked_text_ranges(cx), None);
7124
7125 editor
7126 });
7127 }
7128
7129 #[gpui::test]
7130 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
7131 cx.set_global(Settings::test(cx));
7132
7133 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
7134 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7135 editor.update(cx, |view, cx| {
7136 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7137 });
7138 assert_eq!(
7139 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7140 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7141 );
7142
7143 editor.update(cx, |view, cx| {
7144 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7145 });
7146
7147 assert_eq!(
7148 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7149 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7150 );
7151
7152 editor.update(cx, |view, cx| {
7153 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7154 });
7155
7156 assert_eq!(
7157 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7158 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7159 );
7160
7161 editor.update(cx, |view, cx| {
7162 view.end_selection(cx);
7163 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7164 });
7165
7166 assert_eq!(
7167 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7168 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7169 );
7170
7171 editor.update(cx, |view, cx| {
7172 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
7173 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
7174 });
7175
7176 assert_eq!(
7177 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7178 [
7179 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
7180 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
7181 ]
7182 );
7183
7184 editor.update(cx, |view, cx| {
7185 view.end_selection(cx);
7186 });
7187
7188 assert_eq!(
7189 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7190 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
7191 );
7192 }
7193
7194 #[gpui::test]
7195 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
7196 cx.set_global(Settings::test(cx));
7197 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7198 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7199
7200 view.update(cx, |view, cx| {
7201 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7202 assert_eq!(
7203 view.selections.display_ranges(cx),
7204 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7205 );
7206 });
7207
7208 view.update(cx, |view, cx| {
7209 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7210 assert_eq!(
7211 view.selections.display_ranges(cx),
7212 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7213 );
7214 });
7215
7216 view.update(cx, |view, cx| {
7217 view.cancel(&Cancel, cx);
7218 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7219 assert_eq!(
7220 view.selections.display_ranges(cx),
7221 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7222 );
7223 });
7224 }
7225
7226 #[gpui::test]
7227 fn test_clone(cx: &mut gpui::MutableAppContext) {
7228 let (text, selection_ranges) = marked_text_ranges(
7229 indoc! {"
7230 one
7231 two
7232 threeˇ
7233 four
7234 fiveˇ
7235 "},
7236 true,
7237 );
7238 cx.set_global(Settings::test(cx));
7239 let buffer = MultiBuffer::build_simple(&text, cx);
7240
7241 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7242
7243 editor.update(cx, |editor, cx| {
7244 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
7245 editor.fold_ranges(
7246 [
7247 Point::new(1, 0)..Point::new(2, 0),
7248 Point::new(3, 0)..Point::new(4, 0),
7249 ],
7250 cx,
7251 );
7252 });
7253
7254 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
7255 cx.add_window(Default::default(), |cx| editor.clone(cx))
7256 });
7257
7258 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
7259 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
7260
7261 assert_eq!(
7262 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
7263 editor.update(cx, |e, cx| e.display_text(cx))
7264 );
7265 assert_eq!(
7266 cloned_snapshot
7267 .folds_in_range(0..text.len())
7268 .collect::<Vec<_>>(),
7269 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
7270 );
7271 assert_set_eq!(
7272 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7273 editor.read(cx).selections.ranges(cx)
7274 );
7275 assert_set_eq!(
7276 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7277 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7278 );
7279 }
7280
7281 #[gpui::test]
7282 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7283 cx.set_global(Settings::test(cx));
7284 use workspace::Item;
7285 let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx));
7286 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7287
7288 cx.add_view(&pane, |cx| {
7289 let mut editor = build_editor(buffer.clone(), cx);
7290 let handle = cx.handle();
7291 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7292
7293 fn pop_history(
7294 editor: &mut Editor,
7295 cx: &mut MutableAppContext,
7296 ) -> Option<NavigationEntry> {
7297 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7298 }
7299
7300 // Move the cursor a small distance.
7301 // Nothing is added to the navigation history.
7302 editor.change_selections(None, cx, |s| {
7303 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7304 });
7305 editor.change_selections(None, cx, |s| {
7306 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7307 });
7308 assert!(pop_history(&mut editor, cx).is_none());
7309
7310 // Move the cursor a large distance.
7311 // The history can jump back to the previous position.
7312 editor.change_selections(None, cx, |s| {
7313 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7314 });
7315 let nav_entry = pop_history(&mut editor, cx).unwrap();
7316 editor.navigate(nav_entry.data.unwrap(), cx);
7317 assert_eq!(nav_entry.item.id(), cx.view_id());
7318 assert_eq!(
7319 editor.selections.display_ranges(cx),
7320 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7321 );
7322 assert!(pop_history(&mut editor, cx).is_none());
7323
7324 // Move the cursor a small distance via the mouse.
7325 // Nothing is added to the navigation history.
7326 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7327 editor.end_selection(cx);
7328 assert_eq!(
7329 editor.selections.display_ranges(cx),
7330 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7331 );
7332 assert!(pop_history(&mut editor, cx).is_none());
7333
7334 // Move the cursor a large distance via the mouse.
7335 // The history can jump back to the previous position.
7336 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7337 editor.end_selection(cx);
7338 assert_eq!(
7339 editor.selections.display_ranges(cx),
7340 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7341 );
7342 let nav_entry = pop_history(&mut editor, cx).unwrap();
7343 editor.navigate(nav_entry.data.unwrap(), cx);
7344 assert_eq!(nav_entry.item.id(), cx.view_id());
7345 assert_eq!(
7346 editor.selections.display_ranges(cx),
7347 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7348 );
7349 assert!(pop_history(&mut editor, cx).is_none());
7350
7351 // Set scroll position to check later
7352 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7353 let original_scroll_position = editor.scroll_position;
7354 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7355
7356 // Jump to the end of the document and adjust scroll
7357 editor.move_to_end(&MoveToEnd, cx);
7358 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7359 assert_ne!(editor.scroll_position, original_scroll_position);
7360 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7361
7362 let nav_entry = pop_history(&mut editor, cx).unwrap();
7363 editor.navigate(nav_entry.data.unwrap(), cx);
7364 assert_eq!(editor.scroll_position, original_scroll_position);
7365 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7366
7367 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7368 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7369 invalid_anchor.text_anchor.buffer_id = Some(999);
7370 let invalid_point = Point::new(9999, 0);
7371 editor.navigate(
7372 Box::new(NavigationData {
7373 cursor_anchor: invalid_anchor.clone(),
7374 cursor_position: invalid_point,
7375 scroll_top_anchor: invalid_anchor,
7376 scroll_top_row: invalid_point.row,
7377 scroll_position: Default::default(),
7378 }),
7379 cx,
7380 );
7381 assert_eq!(
7382 editor.selections.display_ranges(cx),
7383 &[editor.max_point(cx)..editor.max_point(cx)]
7384 );
7385 assert_eq!(
7386 editor.scroll_position(cx),
7387 vec2f(0., editor.max_point(cx).row() as f32)
7388 );
7389
7390 editor
7391 });
7392 }
7393
7394 #[gpui::test]
7395 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7396 cx.set_global(Settings::test(cx));
7397 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7398 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7399
7400 view.update(cx, |view, cx| {
7401 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7402 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7403 view.end_selection(cx);
7404
7405 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7406 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7407 view.end_selection(cx);
7408 assert_eq!(
7409 view.selections.display_ranges(cx),
7410 [
7411 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7412 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7413 ]
7414 );
7415 });
7416
7417 view.update(cx, |view, cx| {
7418 view.cancel(&Cancel, cx);
7419 assert_eq!(
7420 view.selections.display_ranges(cx),
7421 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
7422 );
7423 });
7424
7425 view.update(cx, |view, cx| {
7426 view.cancel(&Cancel, cx);
7427 assert_eq!(
7428 view.selections.display_ranges(cx),
7429 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7430 );
7431 });
7432 }
7433
7434 #[gpui::test]
7435 fn test_fold(cx: &mut gpui::MutableAppContext) {
7436 cx.set_global(Settings::test(cx));
7437 let buffer = MultiBuffer::build_simple(
7438 &"
7439 impl Foo {
7440 // Hello!
7441
7442 fn a() {
7443 1
7444 }
7445
7446 fn b() {
7447 2
7448 }
7449
7450 fn c() {
7451 3
7452 }
7453 }
7454 "
7455 .unindent(),
7456 cx,
7457 );
7458 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7459
7460 view.update(cx, |view, cx| {
7461 view.change_selections(None, cx, |s| {
7462 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7463 });
7464 view.fold(&Fold, cx);
7465 assert_eq!(
7466 view.display_text(cx),
7467 "
7468 impl Foo {
7469 // Hello!
7470
7471 fn a() {
7472 1
7473 }
7474
7475 fn b() {…
7476 }
7477
7478 fn c() {…
7479 }
7480 }
7481 "
7482 .unindent(),
7483 );
7484
7485 view.fold(&Fold, cx);
7486 assert_eq!(
7487 view.display_text(cx),
7488 "
7489 impl Foo {…
7490 }
7491 "
7492 .unindent(),
7493 );
7494
7495 view.unfold_lines(&UnfoldLines, cx);
7496 assert_eq!(
7497 view.display_text(cx),
7498 "
7499 impl Foo {
7500 // Hello!
7501
7502 fn a() {
7503 1
7504 }
7505
7506 fn b() {…
7507 }
7508
7509 fn c() {…
7510 }
7511 }
7512 "
7513 .unindent(),
7514 );
7515
7516 view.unfold_lines(&UnfoldLines, cx);
7517 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7518 });
7519 }
7520
7521 #[gpui::test]
7522 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7523 cx.set_global(Settings::test(cx));
7524 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7525 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7526
7527 buffer.update(cx, |buffer, cx| {
7528 buffer.edit(
7529 vec![
7530 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7531 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7532 ],
7533 None,
7534 cx,
7535 );
7536 });
7537
7538 view.update(cx, |view, cx| {
7539 assert_eq!(
7540 view.selections.display_ranges(cx),
7541 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7542 );
7543
7544 view.move_down(&MoveDown, cx);
7545 assert_eq!(
7546 view.selections.display_ranges(cx),
7547 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7548 );
7549
7550 view.move_right(&MoveRight, cx);
7551 assert_eq!(
7552 view.selections.display_ranges(cx),
7553 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7554 );
7555
7556 view.move_left(&MoveLeft, cx);
7557 assert_eq!(
7558 view.selections.display_ranges(cx),
7559 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7560 );
7561
7562 view.move_up(&MoveUp, cx);
7563 assert_eq!(
7564 view.selections.display_ranges(cx),
7565 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7566 );
7567
7568 view.move_to_end(&MoveToEnd, cx);
7569 assert_eq!(
7570 view.selections.display_ranges(cx),
7571 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7572 );
7573
7574 view.move_to_beginning(&MoveToBeginning, cx);
7575 assert_eq!(
7576 view.selections.display_ranges(cx),
7577 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7578 );
7579
7580 view.change_selections(None, cx, |s| {
7581 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7582 });
7583 view.select_to_beginning(&SelectToBeginning, cx);
7584 assert_eq!(
7585 view.selections.display_ranges(cx),
7586 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7587 );
7588
7589 view.select_to_end(&SelectToEnd, cx);
7590 assert_eq!(
7591 view.selections.display_ranges(cx),
7592 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7593 );
7594 });
7595 }
7596
7597 #[gpui::test]
7598 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7599 cx.set_global(Settings::test(cx));
7600 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7601 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7602
7603 assert_eq!('ⓐ'.len_utf8(), 3);
7604 assert_eq!('α'.len_utf8(), 2);
7605
7606 view.update(cx, |view, cx| {
7607 view.fold_ranges(
7608 vec![
7609 Point::new(0, 6)..Point::new(0, 12),
7610 Point::new(1, 2)..Point::new(1, 4),
7611 Point::new(2, 4)..Point::new(2, 8),
7612 ],
7613 cx,
7614 );
7615 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7616
7617 view.move_right(&MoveRight, cx);
7618 assert_eq!(
7619 view.selections.display_ranges(cx),
7620 &[empty_range(0, "ⓐ".len())]
7621 );
7622 view.move_right(&MoveRight, cx);
7623 assert_eq!(
7624 view.selections.display_ranges(cx),
7625 &[empty_range(0, "ⓐⓑ".len())]
7626 );
7627 view.move_right(&MoveRight, cx);
7628 assert_eq!(
7629 view.selections.display_ranges(cx),
7630 &[empty_range(0, "ⓐⓑ…".len())]
7631 );
7632
7633 view.move_down(&MoveDown, cx);
7634 assert_eq!(
7635 view.selections.display_ranges(cx),
7636 &[empty_range(1, "ab…".len())]
7637 );
7638 view.move_left(&MoveLeft, cx);
7639 assert_eq!(
7640 view.selections.display_ranges(cx),
7641 &[empty_range(1, "ab".len())]
7642 );
7643 view.move_left(&MoveLeft, cx);
7644 assert_eq!(
7645 view.selections.display_ranges(cx),
7646 &[empty_range(1, "a".len())]
7647 );
7648
7649 view.move_down(&MoveDown, cx);
7650 assert_eq!(
7651 view.selections.display_ranges(cx),
7652 &[empty_range(2, "α".len())]
7653 );
7654 view.move_right(&MoveRight, cx);
7655 assert_eq!(
7656 view.selections.display_ranges(cx),
7657 &[empty_range(2, "αβ".len())]
7658 );
7659 view.move_right(&MoveRight, cx);
7660 assert_eq!(
7661 view.selections.display_ranges(cx),
7662 &[empty_range(2, "αβ…".len())]
7663 );
7664 view.move_right(&MoveRight, cx);
7665 assert_eq!(
7666 view.selections.display_ranges(cx),
7667 &[empty_range(2, "αβ…ε".len())]
7668 );
7669
7670 view.move_up(&MoveUp, cx);
7671 assert_eq!(
7672 view.selections.display_ranges(cx),
7673 &[empty_range(1, "ab…e".len())]
7674 );
7675 view.move_up(&MoveUp, cx);
7676 assert_eq!(
7677 view.selections.display_ranges(cx),
7678 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7679 );
7680 view.move_left(&MoveLeft, cx);
7681 assert_eq!(
7682 view.selections.display_ranges(cx),
7683 &[empty_range(0, "ⓐⓑ…".len())]
7684 );
7685 view.move_left(&MoveLeft, cx);
7686 assert_eq!(
7687 view.selections.display_ranges(cx),
7688 &[empty_range(0, "ⓐⓑ".len())]
7689 );
7690 view.move_left(&MoveLeft, cx);
7691 assert_eq!(
7692 view.selections.display_ranges(cx),
7693 &[empty_range(0, "ⓐ".len())]
7694 );
7695 });
7696 }
7697
7698 #[gpui::test]
7699 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7700 cx.set_global(Settings::test(cx));
7701 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7702 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7703 view.update(cx, |view, cx| {
7704 view.change_selections(None, cx, |s| {
7705 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7706 });
7707 view.move_down(&MoveDown, cx);
7708 assert_eq!(
7709 view.selections.display_ranges(cx),
7710 &[empty_range(1, "abcd".len())]
7711 );
7712
7713 view.move_down(&MoveDown, cx);
7714 assert_eq!(
7715 view.selections.display_ranges(cx),
7716 &[empty_range(2, "αβγ".len())]
7717 );
7718
7719 view.move_down(&MoveDown, cx);
7720 assert_eq!(
7721 view.selections.display_ranges(cx),
7722 &[empty_range(3, "abcd".len())]
7723 );
7724
7725 view.move_down(&MoveDown, cx);
7726 assert_eq!(
7727 view.selections.display_ranges(cx),
7728 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7729 );
7730
7731 view.move_up(&MoveUp, cx);
7732 assert_eq!(
7733 view.selections.display_ranges(cx),
7734 &[empty_range(3, "abcd".len())]
7735 );
7736
7737 view.move_up(&MoveUp, cx);
7738 assert_eq!(
7739 view.selections.display_ranges(cx),
7740 &[empty_range(2, "αβγ".len())]
7741 );
7742 });
7743 }
7744
7745 #[gpui::test]
7746 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7747 cx.set_global(Settings::test(cx));
7748 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7749 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7750 view.update(cx, |view, cx| {
7751 view.change_selections(None, cx, |s| {
7752 s.select_display_ranges([
7753 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7754 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7755 ]);
7756 });
7757 });
7758
7759 view.update(cx, |view, cx| {
7760 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7761 assert_eq!(
7762 view.selections.display_ranges(cx),
7763 &[
7764 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7765 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7766 ]
7767 );
7768 });
7769
7770 view.update(cx, |view, cx| {
7771 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7772 assert_eq!(
7773 view.selections.display_ranges(cx),
7774 &[
7775 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7776 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7777 ]
7778 );
7779 });
7780
7781 view.update(cx, |view, cx| {
7782 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7783 assert_eq!(
7784 view.selections.display_ranges(cx),
7785 &[
7786 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7787 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7788 ]
7789 );
7790 });
7791
7792 view.update(cx, |view, cx| {
7793 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7794 assert_eq!(
7795 view.selections.display_ranges(cx),
7796 &[
7797 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7798 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7799 ]
7800 );
7801 });
7802
7803 // Moving to the end of line again is a no-op.
7804 view.update(cx, |view, cx| {
7805 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7806 assert_eq!(
7807 view.selections.display_ranges(cx),
7808 &[
7809 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7810 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7811 ]
7812 );
7813 });
7814
7815 view.update(cx, |view, cx| {
7816 view.move_left(&MoveLeft, cx);
7817 view.select_to_beginning_of_line(
7818 &SelectToBeginningOfLine {
7819 stop_at_soft_wraps: true,
7820 },
7821 cx,
7822 );
7823 assert_eq!(
7824 view.selections.display_ranges(cx),
7825 &[
7826 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7827 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7828 ]
7829 );
7830 });
7831
7832 view.update(cx, |view, cx| {
7833 view.select_to_beginning_of_line(
7834 &SelectToBeginningOfLine {
7835 stop_at_soft_wraps: true,
7836 },
7837 cx,
7838 );
7839 assert_eq!(
7840 view.selections.display_ranges(cx),
7841 &[
7842 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7843 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7844 ]
7845 );
7846 });
7847
7848 view.update(cx, |view, cx| {
7849 view.select_to_beginning_of_line(
7850 &SelectToBeginningOfLine {
7851 stop_at_soft_wraps: true,
7852 },
7853 cx,
7854 );
7855 assert_eq!(
7856 view.selections.display_ranges(cx),
7857 &[
7858 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7859 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7860 ]
7861 );
7862 });
7863
7864 view.update(cx, |view, cx| {
7865 view.select_to_end_of_line(
7866 &SelectToEndOfLine {
7867 stop_at_soft_wraps: true,
7868 },
7869 cx,
7870 );
7871 assert_eq!(
7872 view.selections.display_ranges(cx),
7873 &[
7874 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7875 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7876 ]
7877 );
7878 });
7879
7880 view.update(cx, |view, cx| {
7881 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7882 assert_eq!(view.display_text(cx), "ab\n de");
7883 assert_eq!(
7884 view.selections.display_ranges(cx),
7885 &[
7886 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7887 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7888 ]
7889 );
7890 });
7891
7892 view.update(cx, |view, cx| {
7893 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7894 assert_eq!(view.display_text(cx), "\n");
7895 assert_eq!(
7896 view.selections.display_ranges(cx),
7897 &[
7898 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7899 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7900 ]
7901 );
7902 });
7903 }
7904
7905 #[gpui::test]
7906 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7907 cx.set_global(Settings::test(cx));
7908 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7909 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7910 view.update(cx, |view, cx| {
7911 view.change_selections(None, cx, |s| {
7912 s.select_display_ranges([
7913 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7914 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7915 ])
7916 });
7917
7918 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7919 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7920
7921 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7922 assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx);
7923
7924 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7925 assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx);
7926
7927 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7928 assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7929
7930 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7931 assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx);
7932
7933 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7934 assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx);
7935
7936 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7937 assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7938
7939 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7940 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7941
7942 view.move_right(&MoveRight, cx);
7943 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7944 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7945
7946 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7947 assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx);
7948
7949 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7950 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7951 });
7952 }
7953
7954 #[gpui::test]
7955 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7956 cx.set_global(Settings::test(cx));
7957 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7958 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7959
7960 view.update(cx, |view, cx| {
7961 view.set_wrap_width(Some(140.), cx);
7962 assert_eq!(
7963 view.display_text(cx),
7964 "use one::{\n two::three::\n four::five\n};"
7965 );
7966
7967 view.change_selections(None, cx, |s| {
7968 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7969 });
7970
7971 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7972 assert_eq!(
7973 view.selections.display_ranges(cx),
7974 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7975 );
7976
7977 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7978 assert_eq!(
7979 view.selections.display_ranges(cx),
7980 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7981 );
7982
7983 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7984 assert_eq!(
7985 view.selections.display_ranges(cx),
7986 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7987 );
7988
7989 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7990 assert_eq!(
7991 view.selections.display_ranges(cx),
7992 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7993 );
7994
7995 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7996 assert_eq!(
7997 view.selections.display_ranges(cx),
7998 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7999 );
8000
8001 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
8002 assert_eq!(
8003 view.selections.display_ranges(cx),
8004 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
8005 );
8006 });
8007 }
8008
8009 #[gpui::test]
8010 async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
8011 let mut cx = EditorTestContext::new(cx);
8012 cx.set_state("one «two threeˇ» four");
8013 cx.update_editor(|editor, cx| {
8014 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
8015 assert_eq!(editor.text(cx), " four");
8016 });
8017 }
8018
8019 #[gpui::test]
8020 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
8021 cx.set_global(Settings::test(cx));
8022 let buffer = MultiBuffer::build_simple("one two three four", cx);
8023 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
8024
8025 view.update(cx, |view, cx| {
8026 view.change_selections(None, cx, |s| {
8027 s.select_display_ranges([
8028 // an empty selection - the preceding word fragment is deleted
8029 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8030 // characters selected - they are deleted
8031 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
8032 ])
8033 });
8034 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
8035 });
8036
8037 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
8038
8039 view.update(cx, |view, cx| {
8040 view.change_selections(None, cx, |s| {
8041 s.select_display_ranges([
8042 // an empty selection - the following word fragment is deleted
8043 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8044 // characters selected - they are deleted
8045 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
8046 ])
8047 });
8048 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
8049 });
8050
8051 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
8052 }
8053
8054 #[gpui::test]
8055 fn test_newline(cx: &mut gpui::MutableAppContext) {
8056 cx.set_global(Settings::test(cx));
8057 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
8058 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
8059
8060 view.update(cx, |view, cx| {
8061 view.change_selections(None, cx, |s| {
8062 s.select_display_ranges([
8063 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8064 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8065 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
8066 ])
8067 });
8068
8069 view.newline(&Newline, cx);
8070 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
8071 });
8072 }
8073
8074 #[gpui::test]
8075 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
8076 cx.set_global(Settings::test(cx));
8077 let buffer = MultiBuffer::build_simple(
8078 "
8079 a
8080 b(
8081 X
8082 )
8083 c(
8084 X
8085 )
8086 "
8087 .unindent()
8088 .as_str(),
8089 cx,
8090 );
8091
8092 let (_, editor) = cx.add_window(Default::default(), |cx| {
8093 let mut editor = build_editor(buffer.clone(), cx);
8094 editor.change_selections(None, cx, |s| {
8095 s.select_ranges([
8096 Point::new(2, 4)..Point::new(2, 5),
8097 Point::new(5, 4)..Point::new(5, 5),
8098 ])
8099 });
8100 editor
8101 });
8102
8103 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8104 buffer.update(cx, |buffer, cx| {
8105 buffer.edit(
8106 [
8107 (Point::new(1, 2)..Point::new(3, 0), ""),
8108 (Point::new(4, 2)..Point::new(6, 0), ""),
8109 ],
8110 None,
8111 cx,
8112 );
8113 assert_eq!(
8114 buffer.read(cx).text(),
8115 "
8116 a
8117 b()
8118 c()
8119 "
8120 .unindent()
8121 );
8122 });
8123
8124 editor.update(cx, |editor, cx| {
8125 assert_eq!(
8126 editor.selections.ranges(cx),
8127 &[
8128 Point::new(1, 2)..Point::new(1, 2),
8129 Point::new(2, 2)..Point::new(2, 2),
8130 ],
8131 );
8132
8133 editor.newline(&Newline, cx);
8134 assert_eq!(
8135 editor.text(cx),
8136 "
8137 a
8138 b(
8139 )
8140 c(
8141 )
8142 "
8143 .unindent()
8144 );
8145
8146 // The selections are moved after the inserted newlines
8147 assert_eq!(
8148 editor.selections.ranges(cx),
8149 &[
8150 Point::new(2, 0)..Point::new(2, 0),
8151 Point::new(4, 0)..Point::new(4, 0),
8152 ],
8153 );
8154 });
8155 }
8156
8157 #[gpui::test]
8158 async fn test_newline_below(cx: &mut gpui::TestAppContext) {
8159 let mut cx = EditorTestContext::new(cx);
8160 cx.update(|cx| {
8161 cx.update_global::<Settings, _, _>(|settings, _| {
8162 settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
8163 });
8164 });
8165
8166 let language = Arc::new(
8167 Language::new(
8168 LanguageConfig::default(),
8169 Some(tree_sitter_rust::language()),
8170 )
8171 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8172 .unwrap(),
8173 );
8174 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8175
8176 cx.set_state(indoc! {"
8177 const a: ˇA = (
8178 (ˇ
8179 «const_functionˇ»(ˇ),
8180 so«mˇ»et«hˇ»ing_ˇelse,ˇ
8181 )ˇ
8182 ˇ);ˇ
8183 "});
8184 cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
8185 cx.assert_editor_state(indoc! {"
8186 const a: A = (
8187 ˇ
8188 (
8189 ˇ
8190 const_function(),
8191 ˇ
8192 ˇ
8193 something_else,
8194 ˇ
8195 ˇ
8196 ˇ
8197 ˇ
8198 )
8199 ˇ
8200 );
8201 ˇ
8202 ˇ
8203 "});
8204 }
8205
8206 #[gpui::test]
8207 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
8208 cx.set_global(Settings::test(cx));
8209 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
8210 let (_, editor) = cx.add_window(Default::default(), |cx| {
8211 let mut editor = build_editor(buffer.clone(), cx);
8212 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
8213 editor
8214 });
8215
8216 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8217 buffer.update(cx, |buffer, cx| {
8218 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
8219 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
8220 });
8221
8222 editor.update(cx, |editor, cx| {
8223 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
8224
8225 editor.insert("Z", cx);
8226 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
8227
8228 // The selections are moved after the inserted characters
8229 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
8230 });
8231 }
8232
8233 #[gpui::test]
8234 async fn test_tab(cx: &mut gpui::TestAppContext) {
8235 let mut cx = EditorTestContext::new(cx);
8236 cx.update(|cx| {
8237 cx.update_global::<Settings, _, _>(|settings, _| {
8238 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
8239 });
8240 });
8241 cx.set_state(indoc! {"
8242 ˇabˇc
8243 ˇ🏀ˇ🏀ˇefg
8244 dˇ
8245 "});
8246 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8247 cx.assert_editor_state(indoc! {"
8248 ˇab ˇc
8249 ˇ🏀 ˇ🏀 ˇefg
8250 d ˇ
8251 "});
8252
8253 cx.set_state(indoc! {"
8254 a
8255 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8256 "});
8257 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8258 cx.assert_editor_state(indoc! {"
8259 a
8260 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8261 "});
8262 }
8263
8264 #[gpui::test]
8265 async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
8266 let mut cx = EditorTestContext::new(cx);
8267 let language = Arc::new(
8268 Language::new(
8269 LanguageConfig::default(),
8270 Some(tree_sitter_rust::language()),
8271 )
8272 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8273 .unwrap(),
8274 );
8275 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8276
8277 // cursors that are already at the suggested indent level insert
8278 // a soft tab. cursors that are to the left of the suggested indent
8279 // auto-indent their line.
8280 cx.set_state(indoc! {"
8281 ˇ
8282 const a: B = (
8283 c(
8284 d(
8285 ˇ
8286 )
8287 ˇ
8288 ˇ )
8289 );
8290 "});
8291 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8292 cx.assert_editor_state(indoc! {"
8293 ˇ
8294 const a: B = (
8295 c(
8296 d(
8297 ˇ
8298 )
8299 ˇ
8300 ˇ)
8301 );
8302 "});
8303
8304 // handle auto-indent when there are multiple cursors on the same line
8305 cx.set_state(indoc! {"
8306 const a: B = (
8307 c(
8308 ˇ ˇ
8309 ˇ )
8310 );
8311 "});
8312 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8313 cx.assert_editor_state(indoc! {"
8314 const a: B = (
8315 c(
8316 ˇ
8317 ˇ)
8318 );
8319 "});
8320 }
8321
8322 #[gpui::test]
8323 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
8324 let mut cx = EditorTestContext::new(cx);
8325
8326 cx.set_state(indoc! {"
8327 «oneˇ» «twoˇ»
8328 three
8329 four
8330 "});
8331 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8332 cx.assert_editor_state(indoc! {"
8333 «oneˇ» «twoˇ»
8334 three
8335 four
8336 "});
8337
8338 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8339 cx.assert_editor_state(indoc! {"
8340 «oneˇ» «twoˇ»
8341 three
8342 four
8343 "});
8344
8345 // select across line ending
8346 cx.set_state(indoc! {"
8347 one two
8348 t«hree
8349 ˇ» four
8350 "});
8351 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8352 cx.assert_editor_state(indoc! {"
8353 one two
8354 t«hree
8355 ˇ» four
8356 "});
8357
8358 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8359 cx.assert_editor_state(indoc! {"
8360 one two
8361 t«hree
8362 ˇ» four
8363 "});
8364
8365 // Ensure that indenting/outdenting works when the cursor is at column 0.
8366 cx.set_state(indoc! {"
8367 one two
8368 ˇthree
8369 four
8370 "});
8371 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8372 cx.assert_editor_state(indoc! {"
8373 one two
8374 ˇthree
8375 four
8376 "});
8377
8378 cx.set_state(indoc! {"
8379 one two
8380 ˇ three
8381 four
8382 "});
8383 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8384 cx.assert_editor_state(indoc! {"
8385 one two
8386 ˇthree
8387 four
8388 "});
8389 }
8390
8391 #[gpui::test]
8392 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8393 let mut cx = EditorTestContext::new(cx);
8394 cx.update(|cx| {
8395 cx.update_global::<Settings, _, _>(|settings, _| {
8396 settings.editor_overrides.hard_tabs = Some(true);
8397 });
8398 });
8399
8400 // select two ranges on one line
8401 cx.set_state(indoc! {"
8402 «oneˇ» «twoˇ»
8403 three
8404 four
8405 "});
8406 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8407 cx.assert_editor_state(indoc! {"
8408 \t«oneˇ» «twoˇ»
8409 three
8410 four
8411 "});
8412 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8413 cx.assert_editor_state(indoc! {"
8414 \t\t«oneˇ» «twoˇ»
8415 three
8416 four
8417 "});
8418 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8419 cx.assert_editor_state(indoc! {"
8420 \t«oneˇ» «twoˇ»
8421 three
8422 four
8423 "});
8424 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8425 cx.assert_editor_state(indoc! {"
8426 «oneˇ» «twoˇ»
8427 three
8428 four
8429 "});
8430
8431 // select across a line ending
8432 cx.set_state(indoc! {"
8433 one two
8434 t«hree
8435 ˇ»four
8436 "});
8437 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8438 cx.assert_editor_state(indoc! {"
8439 one two
8440 \tt«hree
8441 ˇ»four
8442 "});
8443 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8444 cx.assert_editor_state(indoc! {"
8445 one two
8446 \t\tt«hree
8447 ˇ»four
8448 "});
8449 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8450 cx.assert_editor_state(indoc! {"
8451 one two
8452 \tt«hree
8453 ˇ»four
8454 "});
8455 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8456 cx.assert_editor_state(indoc! {"
8457 one two
8458 t«hree
8459 ˇ»four
8460 "});
8461
8462 // Ensure that indenting/outdenting works when the cursor is at column 0.
8463 cx.set_state(indoc! {"
8464 one two
8465 ˇthree
8466 four
8467 "});
8468 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8469 cx.assert_editor_state(indoc! {"
8470 one two
8471 ˇthree
8472 four
8473 "});
8474 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8475 cx.assert_editor_state(indoc! {"
8476 one two
8477 \tˇthree
8478 four
8479 "});
8480 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8481 cx.assert_editor_state(indoc! {"
8482 one two
8483 ˇthree
8484 four
8485 "});
8486 }
8487
8488 #[gpui::test]
8489 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8490 cx.set_global(
8491 Settings::test(cx)
8492 .with_language_defaults(
8493 "TOML",
8494 EditorSettings {
8495 tab_size: Some(2.try_into().unwrap()),
8496 ..Default::default()
8497 },
8498 )
8499 .with_language_defaults(
8500 "Rust",
8501 EditorSettings {
8502 tab_size: Some(4.try_into().unwrap()),
8503 ..Default::default()
8504 },
8505 ),
8506 );
8507 let toml_language = Arc::new(Language::new(
8508 LanguageConfig {
8509 name: "TOML".into(),
8510 ..Default::default()
8511 },
8512 None,
8513 ));
8514 let rust_language = Arc::new(Language::new(
8515 LanguageConfig {
8516 name: "Rust".into(),
8517 ..Default::default()
8518 },
8519 None,
8520 ));
8521
8522 let toml_buffer = cx
8523 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8524 let rust_buffer = cx.add_model(|cx| {
8525 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8526 });
8527 let multibuffer = cx.add_model(|cx| {
8528 let mut multibuffer = MultiBuffer::new(0);
8529 multibuffer.push_excerpts(
8530 toml_buffer.clone(),
8531 [ExcerptRange {
8532 context: Point::new(0, 0)..Point::new(2, 0),
8533 primary: None,
8534 }],
8535 cx,
8536 );
8537 multibuffer.push_excerpts(
8538 rust_buffer.clone(),
8539 [ExcerptRange {
8540 context: Point::new(0, 0)..Point::new(1, 0),
8541 primary: None,
8542 }],
8543 cx,
8544 );
8545 multibuffer
8546 });
8547
8548 cx.add_window(Default::default(), |cx| {
8549 let mut editor = build_editor(multibuffer, cx);
8550
8551 assert_eq!(
8552 editor.text(cx),
8553 indoc! {"
8554 a = 1
8555 b = 2
8556
8557 const c: usize = 3;
8558 "}
8559 );
8560
8561 select_ranges(
8562 &mut editor,
8563 indoc! {"
8564 «aˇ» = 1
8565 b = 2
8566
8567 «const c:ˇ» usize = 3;
8568 "},
8569 cx,
8570 );
8571
8572 editor.tab(&Tab, cx);
8573 assert_text_with_selections(
8574 &mut editor,
8575 indoc! {"
8576 «aˇ» = 1
8577 b = 2
8578
8579 «const c:ˇ» usize = 3;
8580 "},
8581 cx,
8582 );
8583 editor.tab_prev(&TabPrev, cx);
8584 assert_text_with_selections(
8585 &mut editor,
8586 indoc! {"
8587 «aˇ» = 1
8588 b = 2
8589
8590 «const c:ˇ» usize = 3;
8591 "},
8592 cx,
8593 );
8594
8595 editor
8596 });
8597 }
8598
8599 #[gpui::test]
8600 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8601 let mut cx = EditorTestContext::new(cx);
8602
8603 // Basic backspace
8604 cx.set_state(indoc! {"
8605 onˇe two three
8606 fou«rˇ» five six
8607 seven «ˇeight nine
8608 »ten
8609 "});
8610 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8611 cx.assert_editor_state(indoc! {"
8612 oˇe two three
8613 fouˇ five six
8614 seven ˇten
8615 "});
8616
8617 // Test backspace inside and around indents
8618 cx.set_state(indoc! {"
8619 zero
8620 ˇone
8621 ˇtwo
8622 ˇ ˇ ˇ three
8623 ˇ ˇ four
8624 "});
8625 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8626 cx.assert_editor_state(indoc! {"
8627 zero
8628 ˇone
8629 ˇtwo
8630 ˇ threeˇ four
8631 "});
8632
8633 // Test backspace with line_mode set to true
8634 cx.update_editor(|e, _| e.selections.line_mode = true);
8635 cx.set_state(indoc! {"
8636 The ˇquick ˇbrown
8637 fox jumps over
8638 the lazy dog
8639 ˇThe qu«ick bˇ»rown"});
8640 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8641 cx.assert_editor_state(indoc! {"
8642 ˇfox jumps over
8643 the lazy dogˇ"});
8644 }
8645
8646 #[gpui::test]
8647 async fn test_delete(cx: &mut gpui::TestAppContext) {
8648 let mut cx = EditorTestContext::new(cx);
8649
8650 cx.set_state(indoc! {"
8651 onˇe two three
8652 fou«rˇ» five six
8653 seven «ˇeight nine
8654 »ten
8655 "});
8656 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8657 cx.assert_editor_state(indoc! {"
8658 onˇ two three
8659 fouˇ five six
8660 seven ˇten
8661 "});
8662
8663 // Test backspace with line_mode set to true
8664 cx.update_editor(|e, _| e.selections.line_mode = true);
8665 cx.set_state(indoc! {"
8666 The ˇquick ˇbrown
8667 fox «ˇjum»ps over
8668 the lazy dog
8669 ˇThe qu«ick bˇ»rown"});
8670 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8671 cx.assert_editor_state("ˇthe lazy dogˇ");
8672 }
8673
8674 #[gpui::test]
8675 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8676 cx.set_global(Settings::test(cx));
8677 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8678 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8679 view.update(cx, |view, cx| {
8680 view.change_selections(None, cx, |s| {
8681 s.select_display_ranges([
8682 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8683 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8684 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8685 ])
8686 });
8687 view.delete_line(&DeleteLine, cx);
8688 assert_eq!(view.display_text(cx), "ghi");
8689 assert_eq!(
8690 view.selections.display_ranges(cx),
8691 vec![
8692 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8693 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8694 ]
8695 );
8696 });
8697
8698 cx.set_global(Settings::test(cx));
8699 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8700 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8701 view.update(cx, |view, cx| {
8702 view.change_selections(None, cx, |s| {
8703 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8704 });
8705 view.delete_line(&DeleteLine, cx);
8706 assert_eq!(view.display_text(cx), "ghi\n");
8707 assert_eq!(
8708 view.selections.display_ranges(cx),
8709 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8710 );
8711 });
8712 }
8713
8714 #[gpui::test]
8715 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8716 cx.set_global(Settings::test(cx));
8717 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8718 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8719 view.update(cx, |view, cx| {
8720 view.change_selections(None, cx, |s| {
8721 s.select_display_ranges([
8722 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8723 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8724 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8725 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8726 ])
8727 });
8728 view.duplicate_line(&DuplicateLine, cx);
8729 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8730 assert_eq!(
8731 view.selections.display_ranges(cx),
8732 vec![
8733 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8734 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8735 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8736 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8737 ]
8738 );
8739 });
8740
8741 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8742 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8743 view.update(cx, |view, cx| {
8744 view.change_selections(None, cx, |s| {
8745 s.select_display_ranges([
8746 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8747 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8748 ])
8749 });
8750 view.duplicate_line(&DuplicateLine, cx);
8751 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8752 assert_eq!(
8753 view.selections.display_ranges(cx),
8754 vec![
8755 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8756 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8757 ]
8758 );
8759 });
8760 }
8761
8762 #[gpui::test]
8763 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8764 cx.set_global(Settings::test(cx));
8765 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8766 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8767 view.update(cx, |view, cx| {
8768 view.fold_ranges(
8769 vec![
8770 Point::new(0, 2)..Point::new(1, 2),
8771 Point::new(2, 3)..Point::new(4, 1),
8772 Point::new(7, 0)..Point::new(8, 4),
8773 ],
8774 cx,
8775 );
8776 view.change_selections(None, cx, |s| {
8777 s.select_display_ranges([
8778 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8779 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8780 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8781 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8782 ])
8783 });
8784 assert_eq!(
8785 view.display_text(cx),
8786 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8787 );
8788
8789 view.move_line_up(&MoveLineUp, cx);
8790 assert_eq!(
8791 view.display_text(cx),
8792 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8793 );
8794 assert_eq!(
8795 view.selections.display_ranges(cx),
8796 vec![
8797 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8798 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8799 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8800 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8801 ]
8802 );
8803 });
8804
8805 view.update(cx, |view, cx| {
8806 view.move_line_down(&MoveLineDown, cx);
8807 assert_eq!(
8808 view.display_text(cx),
8809 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8810 );
8811 assert_eq!(
8812 view.selections.display_ranges(cx),
8813 vec![
8814 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8815 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8816 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8817 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8818 ]
8819 );
8820 });
8821
8822 view.update(cx, |view, cx| {
8823 view.move_line_down(&MoveLineDown, cx);
8824 assert_eq!(
8825 view.display_text(cx),
8826 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8827 );
8828 assert_eq!(
8829 view.selections.display_ranges(cx),
8830 vec![
8831 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8832 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8833 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8834 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8835 ]
8836 );
8837 });
8838
8839 view.update(cx, |view, cx| {
8840 view.move_line_up(&MoveLineUp, cx);
8841 assert_eq!(
8842 view.display_text(cx),
8843 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8844 );
8845 assert_eq!(
8846 view.selections.display_ranges(cx),
8847 vec![
8848 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8849 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8850 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8851 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8852 ]
8853 );
8854 });
8855 }
8856
8857 #[gpui::test]
8858 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8859 cx.set_global(Settings::test(cx));
8860 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8861 let snapshot = buffer.read(cx).snapshot(cx);
8862 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8863 editor.update(cx, |editor, cx| {
8864 editor.insert_blocks(
8865 [BlockProperties {
8866 style: BlockStyle::Fixed,
8867 position: snapshot.anchor_after(Point::new(2, 0)),
8868 disposition: BlockDisposition::Below,
8869 height: 1,
8870 render: Arc::new(|_| Empty::new().boxed()),
8871 }],
8872 cx,
8873 );
8874 editor.change_selections(None, cx, |s| {
8875 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8876 });
8877 editor.move_line_down(&MoveLineDown, cx);
8878 });
8879 }
8880
8881 #[gpui::test]
8882 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8883 cx.set_global(Settings::test(cx));
8884
8885 _ = cx
8886 .add_window(Default::default(), |cx| {
8887 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8888
8889 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8890 editor.transpose(&Default::default(), cx);
8891 assert_eq!(editor.text(cx), "bac");
8892 assert_eq!(editor.selections.ranges(cx), [2..2]);
8893
8894 editor.transpose(&Default::default(), cx);
8895 assert_eq!(editor.text(cx), "bca");
8896 assert_eq!(editor.selections.ranges(cx), [3..3]);
8897
8898 editor.transpose(&Default::default(), cx);
8899 assert_eq!(editor.text(cx), "bac");
8900 assert_eq!(editor.selections.ranges(cx), [3..3]);
8901
8902 editor
8903 })
8904 .1;
8905
8906 _ = cx
8907 .add_window(Default::default(), |cx| {
8908 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8909
8910 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8911 editor.transpose(&Default::default(), cx);
8912 assert_eq!(editor.text(cx), "acb\nde");
8913 assert_eq!(editor.selections.ranges(cx), [3..3]);
8914
8915 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8916 editor.transpose(&Default::default(), cx);
8917 assert_eq!(editor.text(cx), "acbd\ne");
8918 assert_eq!(editor.selections.ranges(cx), [5..5]);
8919
8920 editor.transpose(&Default::default(), cx);
8921 assert_eq!(editor.text(cx), "acbde\n");
8922 assert_eq!(editor.selections.ranges(cx), [6..6]);
8923
8924 editor.transpose(&Default::default(), cx);
8925 assert_eq!(editor.text(cx), "acbd\ne");
8926 assert_eq!(editor.selections.ranges(cx), [6..6]);
8927
8928 editor
8929 })
8930 .1;
8931
8932 _ = cx
8933 .add_window(Default::default(), |cx| {
8934 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8935
8936 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8937 editor.transpose(&Default::default(), cx);
8938 assert_eq!(editor.text(cx), "bacd\ne");
8939 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8940
8941 editor.transpose(&Default::default(), cx);
8942 assert_eq!(editor.text(cx), "bcade\n");
8943 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8944
8945 editor.transpose(&Default::default(), cx);
8946 assert_eq!(editor.text(cx), "bcda\ne");
8947 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8948
8949 editor.transpose(&Default::default(), cx);
8950 assert_eq!(editor.text(cx), "bcade\n");
8951 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8952
8953 editor.transpose(&Default::default(), cx);
8954 assert_eq!(editor.text(cx), "bcaed\n");
8955 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8956
8957 editor
8958 })
8959 .1;
8960
8961 _ = cx
8962 .add_window(Default::default(), |cx| {
8963 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8964
8965 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8966 editor.transpose(&Default::default(), cx);
8967 assert_eq!(editor.text(cx), "🏀🍐✋");
8968 assert_eq!(editor.selections.ranges(cx), [8..8]);
8969
8970 editor.transpose(&Default::default(), cx);
8971 assert_eq!(editor.text(cx), "🏀✋🍐");
8972 assert_eq!(editor.selections.ranges(cx), [11..11]);
8973
8974 editor.transpose(&Default::default(), cx);
8975 assert_eq!(editor.text(cx), "🏀🍐✋");
8976 assert_eq!(editor.selections.ranges(cx), [11..11]);
8977
8978 editor
8979 })
8980 .1;
8981 }
8982
8983 #[gpui::test]
8984 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8985 let mut cx = EditorTestContext::new(cx);
8986
8987 cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
8988 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8989 cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
8990
8991 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8992 cx.set_state("two ˇfour ˇsix ˇ");
8993 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8994 cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
8995
8996 // Paste again but with only two cursors. Since the number of cursors doesn't
8997 // match the number of slices in the clipboard, the entire clipboard text
8998 // is pasted at each cursor.
8999 cx.set_state("ˇtwo one✅ four three six five ˇ");
9000 cx.update_editor(|e, cx| {
9001 e.handle_input("( ", cx);
9002 e.paste(&Paste, cx);
9003 e.handle_input(") ", cx);
9004 });
9005 cx.assert_editor_state(indoc! {"
9006 ( one✅
9007 three
9008 five ) ˇtwo one✅ four three six five ( one✅
9009 three
9010 five ) ˇ"});
9011
9012 // Cut with three selections, one of which is full-line.
9013 cx.set_state(indoc! {"
9014 1«2ˇ»3
9015 4ˇ567
9016 «8ˇ»9"});
9017 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9018 cx.assert_editor_state(indoc! {"
9019 1ˇ3
9020 ˇ9"});
9021
9022 // Paste with three selections, noticing how the copied selection that was full-line
9023 // gets inserted before the second cursor.
9024 cx.set_state(indoc! {"
9025 1ˇ3
9026 9ˇ
9027 «oˇ»ne"});
9028 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9029 cx.assert_editor_state(indoc! {"
9030 12ˇ3
9031 4567
9032 9ˇ
9033 8ˇne"});
9034
9035 // Copy with a single cursor only, which writes the whole line into the clipboard.
9036 cx.set_state(indoc! {"
9037 The quick brown
9038 fox juˇmps over
9039 the lazy dog"});
9040 cx.update_editor(|e, cx| e.copy(&Copy, cx));
9041 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
9042
9043 // Paste with three selections, noticing how the copied full-line selection is inserted
9044 // before the empty selections but replaces the selection that is non-empty.
9045 cx.set_state(indoc! {"
9046 Tˇhe quick brown
9047 «foˇ»x jumps over
9048 tˇhe lazy dog"});
9049 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9050 cx.assert_editor_state(indoc! {"
9051 fox jumps over
9052 Tˇhe quick brown
9053 fox jumps over
9054 ˇx jumps over
9055 fox jumps over
9056 tˇhe lazy dog"});
9057 }
9058
9059 #[gpui::test]
9060 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
9061 let mut cx = EditorTestContext::new(cx);
9062 let language = Arc::new(Language::new(
9063 LanguageConfig::default(),
9064 Some(tree_sitter_rust::language()),
9065 ));
9066 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
9067
9068 // Cut an indented block, without the leading whitespace.
9069 cx.set_state(indoc! {"
9070 const a: B = (
9071 c(),
9072 «d(
9073 e,
9074 f
9075 )ˇ»
9076 );
9077 "});
9078 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9079 cx.assert_editor_state(indoc! {"
9080 const a: B = (
9081 c(),
9082 ˇ
9083 );
9084 "});
9085
9086 // Paste it at the same position.
9087 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9088 cx.assert_editor_state(indoc! {"
9089 const a: B = (
9090 c(),
9091 d(
9092 e,
9093 f
9094 )ˇ
9095 );
9096 "});
9097
9098 // Paste it at a line with a lower indent level.
9099 cx.set_state(indoc! {"
9100 ˇ
9101 const a: B = (
9102 c(),
9103 );
9104 "});
9105 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9106 cx.assert_editor_state(indoc! {"
9107 d(
9108 e,
9109 f
9110 )ˇ
9111 const a: B = (
9112 c(),
9113 );
9114 "});
9115
9116 // Cut an indented block, with the leading whitespace.
9117 cx.set_state(indoc! {"
9118 const a: B = (
9119 c(),
9120 « d(
9121 e,
9122 f
9123 )
9124 ˇ»);
9125 "});
9126 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9127 cx.assert_editor_state(indoc! {"
9128 const a: B = (
9129 c(),
9130 ˇ);
9131 "});
9132
9133 // Paste it at the same position.
9134 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9135 cx.assert_editor_state(indoc! {"
9136 const a: B = (
9137 c(),
9138 d(
9139 e,
9140 f
9141 )
9142 ˇ);
9143 "});
9144
9145 // Paste it at a line with a higher indent level.
9146 cx.set_state(indoc! {"
9147 const a: B = (
9148 c(),
9149 d(
9150 e,
9151 fˇ
9152 )
9153 );
9154 "});
9155 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9156 cx.assert_editor_state(indoc! {"
9157 const a: B = (
9158 c(),
9159 d(
9160 e,
9161 f d(
9162 e,
9163 f
9164 )
9165 ˇ
9166 )
9167 );
9168 "});
9169 }
9170
9171 #[gpui::test]
9172 fn test_select_all(cx: &mut gpui::MutableAppContext) {
9173 cx.set_global(Settings::test(cx));
9174 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
9175 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9176 view.update(cx, |view, cx| {
9177 view.select_all(&SelectAll, cx);
9178 assert_eq!(
9179 view.selections.display_ranges(cx),
9180 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
9181 );
9182 });
9183 }
9184
9185 #[gpui::test]
9186 fn test_select_line(cx: &mut gpui::MutableAppContext) {
9187 cx.set_global(Settings::test(cx));
9188 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
9189 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9190 view.update(cx, |view, cx| {
9191 view.change_selections(None, cx, |s| {
9192 s.select_display_ranges([
9193 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9194 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9195 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9196 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
9197 ])
9198 });
9199 view.select_line(&SelectLine, cx);
9200 assert_eq!(
9201 view.selections.display_ranges(cx),
9202 vec![
9203 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
9204 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
9205 ]
9206 );
9207 });
9208
9209 view.update(cx, |view, cx| {
9210 view.select_line(&SelectLine, cx);
9211 assert_eq!(
9212 view.selections.display_ranges(cx),
9213 vec![
9214 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
9215 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
9216 ]
9217 );
9218 });
9219
9220 view.update(cx, |view, cx| {
9221 view.select_line(&SelectLine, cx);
9222 assert_eq!(
9223 view.selections.display_ranges(cx),
9224 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
9225 );
9226 });
9227 }
9228
9229 #[gpui::test]
9230 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
9231 cx.set_global(Settings::test(cx));
9232 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
9233 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9234 view.update(cx, |view, cx| {
9235 view.fold_ranges(
9236 vec![
9237 Point::new(0, 2)..Point::new(1, 2),
9238 Point::new(2, 3)..Point::new(4, 1),
9239 Point::new(7, 0)..Point::new(8, 4),
9240 ],
9241 cx,
9242 );
9243 view.change_selections(None, cx, |s| {
9244 s.select_display_ranges([
9245 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9246 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9247 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9248 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9249 ])
9250 });
9251 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
9252 });
9253
9254 view.update(cx, |view, cx| {
9255 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9256 assert_eq!(
9257 view.display_text(cx),
9258 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9259 );
9260 assert_eq!(
9261 view.selections.display_ranges(cx),
9262 [
9263 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9264 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9265 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9266 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9267 ]
9268 );
9269 });
9270
9271 view.update(cx, |view, cx| {
9272 view.change_selections(None, cx, |s| {
9273 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9274 });
9275 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9276 assert_eq!(
9277 view.display_text(cx),
9278 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9279 );
9280 assert_eq!(
9281 view.selections.display_ranges(cx),
9282 [
9283 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9284 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9285 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9286 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9287 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9288 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9289 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9290 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9291 ]
9292 );
9293 });
9294 }
9295
9296 #[gpui::test]
9297 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9298 cx.set_global(Settings::test(cx));
9299 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9300 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9301
9302 view.update(cx, |view, cx| {
9303 view.change_selections(None, cx, |s| {
9304 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9305 });
9306 });
9307 view.update(cx, |view, cx| {
9308 view.add_selection_above(&AddSelectionAbove, cx);
9309 assert_eq!(
9310 view.selections.display_ranges(cx),
9311 vec![
9312 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9313 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9314 ]
9315 );
9316 });
9317
9318 view.update(cx, |view, cx| {
9319 view.add_selection_above(&AddSelectionAbove, cx);
9320 assert_eq!(
9321 view.selections.display_ranges(cx),
9322 vec![
9323 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9324 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9325 ]
9326 );
9327 });
9328
9329 view.update(cx, |view, cx| {
9330 view.add_selection_below(&AddSelectionBelow, cx);
9331 assert_eq!(
9332 view.selections.display_ranges(cx),
9333 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9334 );
9335
9336 view.undo_selection(&UndoSelection, cx);
9337 assert_eq!(
9338 view.selections.display_ranges(cx),
9339 vec![
9340 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9341 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9342 ]
9343 );
9344
9345 view.redo_selection(&RedoSelection, cx);
9346 assert_eq!(
9347 view.selections.display_ranges(cx),
9348 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9349 );
9350 });
9351
9352 view.update(cx, |view, cx| {
9353 view.add_selection_below(&AddSelectionBelow, cx);
9354 assert_eq!(
9355 view.selections.display_ranges(cx),
9356 vec![
9357 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9358 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9359 ]
9360 );
9361 });
9362
9363 view.update(cx, |view, cx| {
9364 view.add_selection_below(&AddSelectionBelow, cx);
9365 assert_eq!(
9366 view.selections.display_ranges(cx),
9367 vec![
9368 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9369 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9370 ]
9371 );
9372 });
9373
9374 view.update(cx, |view, cx| {
9375 view.change_selections(None, cx, |s| {
9376 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9377 });
9378 });
9379 view.update(cx, |view, cx| {
9380 view.add_selection_below(&AddSelectionBelow, cx);
9381 assert_eq!(
9382 view.selections.display_ranges(cx),
9383 vec![
9384 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9385 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9386 ]
9387 );
9388 });
9389
9390 view.update(cx, |view, cx| {
9391 view.add_selection_below(&AddSelectionBelow, cx);
9392 assert_eq!(
9393 view.selections.display_ranges(cx),
9394 vec![
9395 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9396 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9397 ]
9398 );
9399 });
9400
9401 view.update(cx, |view, cx| {
9402 view.add_selection_above(&AddSelectionAbove, cx);
9403 assert_eq!(
9404 view.selections.display_ranges(cx),
9405 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9406 );
9407 });
9408
9409 view.update(cx, |view, cx| {
9410 view.add_selection_above(&AddSelectionAbove, cx);
9411 assert_eq!(
9412 view.selections.display_ranges(cx),
9413 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9414 );
9415 });
9416
9417 view.update(cx, |view, cx| {
9418 view.change_selections(None, cx, |s| {
9419 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9420 });
9421 view.add_selection_below(&AddSelectionBelow, cx);
9422 assert_eq!(
9423 view.selections.display_ranges(cx),
9424 vec![
9425 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9426 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9427 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9428 ]
9429 );
9430 });
9431
9432 view.update(cx, |view, cx| {
9433 view.add_selection_below(&AddSelectionBelow, cx);
9434 assert_eq!(
9435 view.selections.display_ranges(cx),
9436 vec![
9437 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9438 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9439 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9440 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9441 ]
9442 );
9443 });
9444
9445 view.update(cx, |view, cx| {
9446 view.add_selection_above(&AddSelectionAbove, cx);
9447 assert_eq!(
9448 view.selections.display_ranges(cx),
9449 vec![
9450 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9451 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9452 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9453 ]
9454 );
9455 });
9456
9457 view.update(cx, |view, cx| {
9458 view.change_selections(None, cx, |s| {
9459 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9460 });
9461 });
9462 view.update(cx, |view, cx| {
9463 view.add_selection_above(&AddSelectionAbove, cx);
9464 assert_eq!(
9465 view.selections.display_ranges(cx),
9466 vec![
9467 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9468 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9469 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9470 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9471 ]
9472 );
9473 });
9474
9475 view.update(cx, |view, cx| {
9476 view.add_selection_below(&AddSelectionBelow, cx);
9477 assert_eq!(
9478 view.selections.display_ranges(cx),
9479 vec![
9480 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9481 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9482 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9483 ]
9484 );
9485 });
9486 }
9487
9488 #[gpui::test]
9489 async fn test_select_next(cx: &mut gpui::TestAppContext) {
9490 let mut cx = EditorTestContext::new(cx);
9491 cx.set_state("abc\nˇabc abc\ndefabc\nabc");
9492
9493 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9494 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9495
9496 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9497 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9498
9499 cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
9500 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9501
9502 cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
9503 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9504
9505 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9506 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9507
9508 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9509 cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9510 }
9511
9512 #[gpui::test]
9513 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9514 cx.update(|cx| cx.set_global(Settings::test(cx)));
9515 let language = Arc::new(Language::new(
9516 LanguageConfig::default(),
9517 Some(tree_sitter_rust::language()),
9518 ));
9519
9520 let text = r#"
9521 use mod1::mod2::{mod3, mod4};
9522
9523 fn fn_1(param1: bool, param2: &str) {
9524 let var1 = "text";
9525 }
9526 "#
9527 .unindent();
9528
9529 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9530 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9531 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9532 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9533 .await;
9534
9535 view.update(cx, |view, cx| {
9536 view.change_selections(None, cx, |s| {
9537 s.select_display_ranges([
9538 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9539 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9540 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9541 ]);
9542 });
9543 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9544 });
9545 assert_eq!(
9546 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9547 &[
9548 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9549 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9550 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9551 ]
9552 );
9553
9554 view.update(cx, |view, cx| {
9555 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9556 });
9557 assert_eq!(
9558 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9559 &[
9560 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9561 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9562 ]
9563 );
9564
9565 view.update(cx, |view, cx| {
9566 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9567 });
9568 assert_eq!(
9569 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9570 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9571 );
9572
9573 // Trying to expand the selected syntax node one more time has no effect.
9574 view.update(cx, |view, cx| {
9575 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9576 });
9577 assert_eq!(
9578 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9579 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9580 );
9581
9582 view.update(cx, |view, cx| {
9583 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9584 });
9585 assert_eq!(
9586 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9587 &[
9588 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9589 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9590 ]
9591 );
9592
9593 view.update(cx, |view, cx| {
9594 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9595 });
9596 assert_eq!(
9597 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9598 &[
9599 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9600 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9601 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9602 ]
9603 );
9604
9605 view.update(cx, |view, cx| {
9606 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9607 });
9608 assert_eq!(
9609 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9610 &[
9611 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9612 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9613 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9614 ]
9615 );
9616
9617 // Trying to shrink the selected syntax node one more time has no effect.
9618 view.update(cx, |view, cx| {
9619 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9620 });
9621 assert_eq!(
9622 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9623 &[
9624 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9625 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9626 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9627 ]
9628 );
9629
9630 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9631 // a fold.
9632 view.update(cx, |view, cx| {
9633 view.fold_ranges(
9634 vec![
9635 Point::new(0, 21)..Point::new(0, 24),
9636 Point::new(3, 20)..Point::new(3, 22),
9637 ],
9638 cx,
9639 );
9640 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9641 });
9642 assert_eq!(
9643 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9644 &[
9645 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9646 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9647 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9648 ]
9649 );
9650 }
9651
9652 #[gpui::test]
9653 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9654 cx.update(|cx| cx.set_global(Settings::test(cx)));
9655 let language = Arc::new(
9656 Language::new(
9657 LanguageConfig {
9658 brackets: vec![
9659 BracketPair {
9660 start: "{".to_string(),
9661 end: "}".to_string(),
9662 close: false,
9663 newline: true,
9664 },
9665 BracketPair {
9666 start: "(".to_string(),
9667 end: ")".to_string(),
9668 close: false,
9669 newline: true,
9670 },
9671 ],
9672 ..Default::default()
9673 },
9674 Some(tree_sitter_rust::language()),
9675 )
9676 .with_indents_query(
9677 r#"
9678 (_ "(" ")" @end) @indent
9679 (_ "{" "}" @end) @indent
9680 "#,
9681 )
9682 .unwrap(),
9683 );
9684
9685 let text = "fn a() {}";
9686
9687 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9688 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9689 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9690 editor
9691 .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9692 .await;
9693
9694 editor.update(cx, |editor, cx| {
9695 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9696 editor.newline(&Newline, cx);
9697 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9698 assert_eq!(
9699 editor.selections.ranges(cx),
9700 &[
9701 Point::new(1, 4)..Point::new(1, 4),
9702 Point::new(3, 4)..Point::new(3, 4),
9703 Point::new(5, 0)..Point::new(5, 0)
9704 ]
9705 );
9706 });
9707 }
9708
9709 #[gpui::test]
9710 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9711 let mut cx = EditorTestContext::new(cx);
9712
9713 let language = Arc::new(Language::new(
9714 LanguageConfig {
9715 brackets: vec![
9716 BracketPair {
9717 start: "{".to_string(),
9718 end: "}".to_string(),
9719 close: true,
9720 newline: true,
9721 },
9722 BracketPair {
9723 start: "/*".to_string(),
9724 end: " */".to_string(),
9725 close: true,
9726 newline: true,
9727 },
9728 BracketPair {
9729 start: "[".to_string(),
9730 end: "]".to_string(),
9731 close: false,
9732 newline: true,
9733 },
9734 ],
9735 autoclose_before: "})]".to_string(),
9736 ..Default::default()
9737 },
9738 Some(tree_sitter_rust::language()),
9739 ));
9740
9741 let registry = Arc::new(LanguageRegistry::test());
9742 registry.add(language.clone());
9743 cx.update_buffer(|buffer, cx| {
9744 buffer.set_language_registry(registry);
9745 buffer.set_language(Some(language), cx);
9746 });
9747
9748 cx.set_state(
9749 &r#"
9750 🏀ˇ
9751 εˇ
9752 ❤️ˇ
9753 "#
9754 .unindent(),
9755 );
9756
9757 // autoclose multiple nested brackets at multiple cursors
9758 cx.update_editor(|view, cx| {
9759 view.handle_input("{", cx);
9760 view.handle_input("{", cx);
9761 view.handle_input("{", cx);
9762 });
9763 cx.assert_editor_state(
9764 &"
9765 🏀{{{ˇ}}}
9766 ε{{{ˇ}}}
9767 ❤️{{{ˇ}}}
9768 "
9769 .unindent(),
9770 );
9771
9772 // skip over the auto-closed brackets when typing a closing bracket
9773 cx.update_editor(|view, cx| {
9774 view.move_right(&MoveRight, cx);
9775 view.handle_input("}", cx);
9776 view.handle_input("}", cx);
9777 view.handle_input("}", cx);
9778 });
9779 cx.assert_editor_state(
9780 &"
9781 🏀{{{}}}}ˇ
9782 ε{{{}}}}ˇ
9783 ❤️{{{}}}}ˇ
9784 "
9785 .unindent(),
9786 );
9787
9788 // autoclose multi-character pairs
9789 cx.set_state(
9790 &"
9791 ˇ
9792 ˇ
9793 "
9794 .unindent(),
9795 );
9796 cx.update_editor(|view, cx| {
9797 view.handle_input("/", cx);
9798 view.handle_input("*", cx);
9799 });
9800 cx.assert_editor_state(
9801 &"
9802 /*ˇ */
9803 /*ˇ */
9804 "
9805 .unindent(),
9806 );
9807
9808 // one cursor autocloses a multi-character pair, one cursor
9809 // does not autoclose.
9810 cx.set_state(
9811 &"
9812 /ˇ
9813 ˇ
9814 "
9815 .unindent(),
9816 );
9817 cx.update_editor(|view, cx| view.handle_input("*", cx));
9818 cx.assert_editor_state(
9819 &"
9820 /*ˇ */
9821 *ˇ
9822 "
9823 .unindent(),
9824 );
9825
9826 // Don't autoclose if the next character isn't whitespace and isn't
9827 // listed in the language's "autoclose_before" section.
9828 cx.set_state("ˇa b");
9829 cx.update_editor(|view, cx| view.handle_input("{", cx));
9830 cx.assert_editor_state("{ˇa b");
9831
9832 // Surround with brackets if text is selected
9833 cx.set_state("«aˇ» b");
9834 cx.update_editor(|view, cx| view.handle_input("{", cx));
9835 cx.assert_editor_state("{«aˇ»} b");
9836 }
9837
9838 #[gpui::test]
9839 async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
9840 let mut cx = EditorTestContext::new(cx);
9841
9842 let html_language = Arc::new(
9843 Language::new(
9844 LanguageConfig {
9845 name: "HTML".into(),
9846 brackets: vec![
9847 BracketPair {
9848 start: "<".into(),
9849 end: ">".into(),
9850 ..Default::default()
9851 },
9852 BracketPair {
9853 start: "{".into(),
9854 end: "}".into(),
9855 ..Default::default()
9856 },
9857 BracketPair {
9858 start: "(".into(),
9859 end: ")".into(),
9860 ..Default::default()
9861 },
9862 ],
9863 autoclose_before: "})]>".into(),
9864 ..Default::default()
9865 },
9866 Some(tree_sitter_html::language()),
9867 )
9868 .with_injection_query(
9869 r#"
9870 (script_element
9871 (raw_text) @content
9872 (#set! "language" "javascript"))
9873 "#,
9874 )
9875 .unwrap(),
9876 );
9877
9878 let javascript_language = Arc::new(Language::new(
9879 LanguageConfig {
9880 name: "JavaScript".into(),
9881 brackets: vec![
9882 BracketPair {
9883 start: "/*".into(),
9884 end: " */".into(),
9885 ..Default::default()
9886 },
9887 BracketPair {
9888 start: "{".into(),
9889 end: "}".into(),
9890 ..Default::default()
9891 },
9892 BracketPair {
9893 start: "(".into(),
9894 end: ")".into(),
9895 ..Default::default()
9896 },
9897 ],
9898 autoclose_before: "})]>".into(),
9899 ..Default::default()
9900 },
9901 Some(tree_sitter_javascript::language()),
9902 ));
9903
9904 let registry = Arc::new(LanguageRegistry::test());
9905 registry.add(html_language.clone());
9906 registry.add(javascript_language.clone());
9907
9908 cx.update_buffer(|buffer, cx| {
9909 buffer.set_language_registry(registry);
9910 buffer.set_language(Some(html_language), cx);
9911 });
9912
9913 cx.set_state(
9914 &r#"
9915 <body>ˇ
9916 <script>
9917 var x = 1;ˇ
9918 </script>
9919 </body>ˇ
9920 "#
9921 .unindent(),
9922 );
9923
9924 // Precondition: different languages are active at different locations.
9925 cx.update_editor(|editor, cx| {
9926 let snapshot = editor.snapshot(cx);
9927 let cursors = editor.selections.ranges::<usize>(cx);
9928 let languages = cursors
9929 .iter()
9930 .map(|c| snapshot.language_at(c.start).unwrap().name())
9931 .collect::<Vec<_>>();
9932 assert_eq!(
9933 languages,
9934 &["HTML".into(), "JavaScript".into(), "HTML".into()]
9935 );
9936 });
9937
9938 // Angle brackets autoclose in HTML, but not JavaScript.
9939 cx.update_editor(|editor, cx| {
9940 editor.handle_input("<", cx);
9941 editor.handle_input("a", cx);
9942 });
9943 cx.assert_editor_state(
9944 &r#"
9945 <body><aˇ>
9946 <script>
9947 var x = 1;<aˇ
9948 </script>
9949 </body><aˇ>
9950 "#
9951 .unindent(),
9952 );
9953
9954 // Curly braces and parens autoclose in both HTML and JavaScript.
9955 cx.update_editor(|editor, cx| {
9956 editor.handle_input(" b=", cx);
9957 editor.handle_input("{", cx);
9958 editor.handle_input("c", cx);
9959 editor.handle_input("(", cx);
9960 });
9961 cx.assert_editor_state(
9962 &r#"
9963 <body><a b={c(ˇ)}>
9964 <script>
9965 var x = 1;<a b={c(ˇ)}
9966 </script>
9967 </body><a b={c(ˇ)}>
9968 "#
9969 .unindent(),
9970 );
9971
9972 // Brackets that were already autoclosed are skipped.
9973 cx.update_editor(|editor, cx| {
9974 editor.handle_input(")", cx);
9975 editor.handle_input("d", cx);
9976 editor.handle_input("}", cx);
9977 });
9978 cx.assert_editor_state(
9979 &r#"
9980 <body><a b={c()d}ˇ>
9981 <script>
9982 var x = 1;<a b={c()d}ˇ
9983 </script>
9984 </body><a b={c()d}ˇ>
9985 "#
9986 .unindent(),
9987 );
9988 cx.update_editor(|editor, cx| {
9989 editor.handle_input(">", cx);
9990 });
9991 cx.assert_editor_state(
9992 &r#"
9993 <body><a b={c()d}>ˇ
9994 <script>
9995 var x = 1;<a b={c()d}>ˇ
9996 </script>
9997 </body><a b={c()d}>ˇ
9998 "#
9999 .unindent(),
10000 );
10001
10002 // Reset
10003 cx.set_state(
10004 &r#"
10005 <body>ˇ
10006 <script>
10007 var x = 1;ˇ
10008 </script>
10009 </body>ˇ
10010 "#
10011 .unindent(),
10012 );
10013
10014 cx.update_editor(|editor, cx| {
10015 editor.handle_input("<", cx);
10016 });
10017 cx.assert_editor_state(
10018 &r#"
10019 <body><ˇ>
10020 <script>
10021 var x = 1;<ˇ
10022 </script>
10023 </body><ˇ>
10024 "#
10025 .unindent(),
10026 );
10027
10028 // When backspacing, the closing angle brackets are removed.
10029 cx.update_editor(|editor, cx| {
10030 editor.backspace(&Backspace, cx);
10031 });
10032 cx.assert_editor_state(
10033 &r#"
10034 <body>ˇ
10035 <script>
10036 var x = 1;ˇ
10037 </script>
10038 </body>ˇ
10039 "#
10040 .unindent(),
10041 );
10042
10043 // Block comments autoclose in JavaScript, but not HTML.
10044 cx.update_editor(|editor, cx| {
10045 editor.handle_input("/", cx);
10046 editor.handle_input("*", cx);
10047 });
10048 cx.assert_editor_state(
10049 &r#"
10050 <body>/*ˇ
10051 <script>
10052 var x = 1;/*ˇ */
10053 </script>
10054 </body>/*ˇ
10055 "#
10056 .unindent(),
10057 );
10058 }
10059
10060 #[gpui::test]
10061 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
10062 cx.update(|cx| cx.set_global(Settings::test(cx)));
10063 let language = Arc::new(Language::new(
10064 LanguageConfig {
10065 brackets: vec![BracketPair {
10066 start: "{".to_string(),
10067 end: "}".to_string(),
10068 close: true,
10069 newline: true,
10070 }],
10071 ..Default::default()
10072 },
10073 Some(tree_sitter_rust::language()),
10074 ));
10075
10076 let text = r#"
10077 a
10078 b
10079 c
10080 "#
10081 .unindent();
10082
10083 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10084 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10085 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10086 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10087 .await;
10088
10089 view.update(cx, |view, cx| {
10090 view.change_selections(None, cx, |s| {
10091 s.select_display_ranges([
10092 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
10093 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
10094 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
10095 ])
10096 });
10097
10098 view.handle_input("{", cx);
10099 view.handle_input("{", cx);
10100 view.handle_input("{", cx);
10101 assert_eq!(
10102 view.text(cx),
10103 "
10104 {{{a}}}
10105 {{{b}}}
10106 {{{c}}}
10107 "
10108 .unindent()
10109 );
10110 assert_eq!(
10111 view.selections.display_ranges(cx),
10112 [
10113 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
10114 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
10115 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
10116 ]
10117 );
10118
10119 view.undo(&Undo, cx);
10120 assert_eq!(
10121 view.text(cx),
10122 "
10123 a
10124 b
10125 c
10126 "
10127 .unindent()
10128 );
10129 assert_eq!(
10130 view.selections.display_ranges(cx),
10131 [
10132 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
10133 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
10134 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
10135 ]
10136 );
10137 });
10138 }
10139
10140 #[gpui::test]
10141 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
10142 cx.update(|cx| cx.set_global(Settings::test(cx)));
10143 let language = Arc::new(Language::new(
10144 LanguageConfig {
10145 brackets: vec![BracketPair {
10146 start: "{".to_string(),
10147 end: "}".to_string(),
10148 close: true,
10149 newline: true,
10150 }],
10151 autoclose_before: "}".to_string(),
10152 ..Default::default()
10153 },
10154 Some(tree_sitter_rust::language()),
10155 ));
10156
10157 let text = r#"
10158 a
10159 b
10160 c
10161 "#
10162 .unindent();
10163
10164 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10165 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10166 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10167 editor
10168 .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10169 .await;
10170
10171 editor.update(cx, |editor, cx| {
10172 editor.change_selections(None, cx, |s| {
10173 s.select_ranges([
10174 Point::new(0, 1)..Point::new(0, 1),
10175 Point::new(1, 1)..Point::new(1, 1),
10176 Point::new(2, 1)..Point::new(2, 1),
10177 ])
10178 });
10179
10180 editor.handle_input("{", cx);
10181 editor.handle_input("{", cx);
10182 editor.handle_input("_", cx);
10183 assert_eq!(
10184 editor.text(cx),
10185 "
10186 a{{_}}
10187 b{{_}}
10188 c{{_}}
10189 "
10190 .unindent()
10191 );
10192 assert_eq!(
10193 editor.selections.ranges::<Point>(cx),
10194 [
10195 Point::new(0, 4)..Point::new(0, 4),
10196 Point::new(1, 4)..Point::new(1, 4),
10197 Point::new(2, 4)..Point::new(2, 4)
10198 ]
10199 );
10200
10201 editor.backspace(&Default::default(), cx);
10202 editor.backspace(&Default::default(), cx);
10203 assert_eq!(
10204 editor.text(cx),
10205 "
10206 a{}
10207 b{}
10208 c{}
10209 "
10210 .unindent()
10211 );
10212 assert_eq!(
10213 editor.selections.ranges::<Point>(cx),
10214 [
10215 Point::new(0, 2)..Point::new(0, 2),
10216 Point::new(1, 2)..Point::new(1, 2),
10217 Point::new(2, 2)..Point::new(2, 2)
10218 ]
10219 );
10220
10221 editor.delete_to_previous_word_start(&Default::default(), cx);
10222 assert_eq!(
10223 editor.text(cx),
10224 "
10225 a
10226 b
10227 c
10228 "
10229 .unindent()
10230 );
10231 assert_eq!(
10232 editor.selections.ranges::<Point>(cx),
10233 [
10234 Point::new(0, 1)..Point::new(0, 1),
10235 Point::new(1, 1)..Point::new(1, 1),
10236 Point::new(2, 1)..Point::new(2, 1)
10237 ]
10238 );
10239 });
10240 }
10241
10242 #[gpui::test]
10243 async fn test_snippets(cx: &mut gpui::TestAppContext) {
10244 cx.update(|cx| cx.set_global(Settings::test(cx)));
10245
10246 let (text, insertion_ranges) = marked_text_ranges(
10247 indoc! {"
10248 a.ˇ b
10249 a.ˇ b
10250 a.ˇ b
10251 "},
10252 false,
10253 );
10254
10255 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
10256 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10257
10258 editor.update(cx, |editor, cx| {
10259 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
10260
10261 editor
10262 .insert_snippet(&insertion_ranges, snippet, cx)
10263 .unwrap();
10264
10265 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
10266 let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
10267 assert_eq!(editor.text(cx), expected_text);
10268 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
10269 }
10270
10271 assert(
10272 editor,
10273 cx,
10274 indoc! {"
10275 a.f(«one», two, «three») b
10276 a.f(«one», two, «three») b
10277 a.f(«one», two, «three») b
10278 "},
10279 );
10280
10281 // Can't move earlier than the first tab stop
10282 assert!(!editor.move_to_prev_snippet_tabstop(cx));
10283 assert(
10284 editor,
10285 cx,
10286 indoc! {"
10287 a.f(«one», two, «three») b
10288 a.f(«one», two, «three») b
10289 a.f(«one», two, «three») b
10290 "},
10291 );
10292
10293 assert!(editor.move_to_next_snippet_tabstop(cx));
10294 assert(
10295 editor,
10296 cx,
10297 indoc! {"
10298 a.f(one, «two», three) b
10299 a.f(one, «two», three) b
10300 a.f(one, «two», three) b
10301 "},
10302 );
10303
10304 editor.move_to_prev_snippet_tabstop(cx);
10305 assert(
10306 editor,
10307 cx,
10308 indoc! {"
10309 a.f(«one», two, «three») b
10310 a.f(«one», two, «three») b
10311 a.f(«one», two, «three») b
10312 "},
10313 );
10314
10315 assert!(editor.move_to_next_snippet_tabstop(cx));
10316 assert(
10317 editor,
10318 cx,
10319 indoc! {"
10320 a.f(one, «two», three) b
10321 a.f(one, «two», three) b
10322 a.f(one, «two», three) b
10323 "},
10324 );
10325 assert!(editor.move_to_next_snippet_tabstop(cx));
10326 assert(
10327 editor,
10328 cx,
10329 indoc! {"
10330 a.f(one, two, three)ˇ b
10331 a.f(one, two, three)ˇ b
10332 a.f(one, two, three)ˇ b
10333 "},
10334 );
10335
10336 // As soon as the last tab stop is reached, snippet state is gone
10337 editor.move_to_prev_snippet_tabstop(cx);
10338 assert(
10339 editor,
10340 cx,
10341 indoc! {"
10342 a.f(one, two, three)ˇ b
10343 a.f(one, two, three)ˇ b
10344 a.f(one, two, three)ˇ b
10345 "},
10346 );
10347 });
10348 }
10349
10350 #[gpui::test]
10351 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
10352 cx.foreground().forbid_parking();
10353
10354 let mut language = Language::new(
10355 LanguageConfig {
10356 name: "Rust".into(),
10357 path_suffixes: vec!["rs".to_string()],
10358 ..Default::default()
10359 },
10360 Some(tree_sitter_rust::language()),
10361 );
10362 let mut fake_servers = language
10363 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10364 capabilities: lsp::ServerCapabilities {
10365 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10366 ..Default::default()
10367 },
10368 ..Default::default()
10369 }))
10370 .await;
10371
10372 let fs = FakeFs::new(cx.background());
10373 fs.insert_file("/file.rs", Default::default()).await;
10374
10375 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10376 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10377 let buffer = project
10378 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10379 .await
10380 .unwrap();
10381
10382 cx.foreground().start_waiting();
10383 let fake_server = fake_servers.next().await.unwrap();
10384
10385 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10386 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10387 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10388 assert!(cx.read(|cx| editor.is_dirty(cx)));
10389
10390 let save = cx.update(|cx| editor.save(project.clone(), cx));
10391 fake_server
10392 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10393 assert_eq!(
10394 params.text_document.uri,
10395 lsp::Url::from_file_path("/file.rs").unwrap()
10396 );
10397 assert_eq!(params.options.tab_size, 4);
10398 Ok(Some(vec![lsp::TextEdit::new(
10399 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10400 ", ".to_string(),
10401 )]))
10402 })
10403 .next()
10404 .await;
10405 cx.foreground().start_waiting();
10406 save.await.unwrap();
10407 assert_eq!(
10408 editor.read_with(cx, |editor, cx| editor.text(cx)),
10409 "one, two\nthree\n"
10410 );
10411 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10412
10413 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10414 assert!(cx.read(|cx| editor.is_dirty(cx)));
10415
10416 // Ensure we can still save even if formatting hangs.
10417 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10418 assert_eq!(
10419 params.text_document.uri,
10420 lsp::Url::from_file_path("/file.rs").unwrap()
10421 );
10422 futures::future::pending::<()>().await;
10423 unreachable!()
10424 });
10425 let save = cx.update(|cx| editor.save(project.clone(), cx));
10426 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10427 cx.foreground().start_waiting();
10428 save.await.unwrap();
10429 assert_eq!(
10430 editor.read_with(cx, |editor, cx| editor.text(cx)),
10431 "one\ntwo\nthree\n"
10432 );
10433 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10434
10435 // Set rust language override and assert overriden tabsize is sent to language server
10436 cx.update(|cx| {
10437 cx.update_global::<Settings, _, _>(|settings, _| {
10438 settings.language_overrides.insert(
10439 "Rust".into(),
10440 EditorSettings {
10441 tab_size: Some(8.try_into().unwrap()),
10442 ..Default::default()
10443 },
10444 );
10445 })
10446 });
10447
10448 let save = cx.update(|cx| editor.save(project.clone(), cx));
10449 fake_server
10450 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10451 assert_eq!(
10452 params.text_document.uri,
10453 lsp::Url::from_file_path("/file.rs").unwrap()
10454 );
10455 assert_eq!(params.options.tab_size, 8);
10456 Ok(Some(vec![]))
10457 })
10458 .next()
10459 .await;
10460 cx.foreground().start_waiting();
10461 save.await.unwrap();
10462 }
10463
10464 #[gpui::test]
10465 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10466 cx.foreground().forbid_parking();
10467
10468 let mut language = Language::new(
10469 LanguageConfig {
10470 name: "Rust".into(),
10471 path_suffixes: vec!["rs".to_string()],
10472 ..Default::default()
10473 },
10474 Some(tree_sitter_rust::language()),
10475 );
10476 let mut fake_servers = language
10477 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10478 capabilities: lsp::ServerCapabilities {
10479 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10480 ..Default::default()
10481 },
10482 ..Default::default()
10483 }))
10484 .await;
10485
10486 let fs = FakeFs::new(cx.background());
10487 fs.insert_file("/file.rs", Default::default()).await;
10488
10489 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10490 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10491 let buffer = project
10492 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10493 .await
10494 .unwrap();
10495
10496 cx.foreground().start_waiting();
10497 let fake_server = fake_servers.next().await.unwrap();
10498
10499 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10500 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10501 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10502 assert!(cx.read(|cx| editor.is_dirty(cx)));
10503
10504 let save = cx.update(|cx| editor.save(project.clone(), cx));
10505 fake_server
10506 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10507 assert_eq!(
10508 params.text_document.uri,
10509 lsp::Url::from_file_path("/file.rs").unwrap()
10510 );
10511 assert_eq!(params.options.tab_size, 4);
10512 Ok(Some(vec![lsp::TextEdit::new(
10513 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10514 ", ".to_string(),
10515 )]))
10516 })
10517 .next()
10518 .await;
10519 cx.foreground().start_waiting();
10520 save.await.unwrap();
10521 assert_eq!(
10522 editor.read_with(cx, |editor, cx| editor.text(cx)),
10523 "one, two\nthree\n"
10524 );
10525 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10526
10527 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10528 assert!(cx.read(|cx| editor.is_dirty(cx)));
10529
10530 // Ensure we can still save even if formatting hangs.
10531 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10532 move |params, _| async move {
10533 assert_eq!(
10534 params.text_document.uri,
10535 lsp::Url::from_file_path("/file.rs").unwrap()
10536 );
10537 futures::future::pending::<()>().await;
10538 unreachable!()
10539 },
10540 );
10541 let save = cx.update(|cx| editor.save(project.clone(), cx));
10542 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10543 cx.foreground().start_waiting();
10544 save.await.unwrap();
10545 assert_eq!(
10546 editor.read_with(cx, |editor, cx| editor.text(cx)),
10547 "one\ntwo\nthree\n"
10548 );
10549 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10550
10551 // Set rust language override and assert overriden tabsize is sent to language server
10552 cx.update(|cx| {
10553 cx.update_global::<Settings, _, _>(|settings, _| {
10554 settings.language_overrides.insert(
10555 "Rust".into(),
10556 EditorSettings {
10557 tab_size: Some(8.try_into().unwrap()),
10558 ..Default::default()
10559 },
10560 );
10561 })
10562 });
10563
10564 let save = cx.update(|cx| editor.save(project.clone(), cx));
10565 fake_server
10566 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10567 assert_eq!(
10568 params.text_document.uri,
10569 lsp::Url::from_file_path("/file.rs").unwrap()
10570 );
10571 assert_eq!(params.options.tab_size, 8);
10572 Ok(Some(vec![]))
10573 })
10574 .next()
10575 .await;
10576 cx.foreground().start_waiting();
10577 save.await.unwrap();
10578 }
10579
10580 #[gpui::test]
10581 async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
10582 cx.foreground().forbid_parking();
10583
10584 let mut language = Language::new(
10585 LanguageConfig {
10586 name: "Rust".into(),
10587 path_suffixes: vec!["rs".to_string()],
10588 ..Default::default()
10589 },
10590 Some(tree_sitter_rust::language()),
10591 );
10592 let mut fake_servers = language
10593 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10594 capabilities: lsp::ServerCapabilities {
10595 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10596 ..Default::default()
10597 },
10598 ..Default::default()
10599 }))
10600 .await;
10601
10602 let fs = FakeFs::new(cx.background());
10603 fs.insert_file("/file.rs", Default::default()).await;
10604
10605 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10606 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10607 let buffer = project
10608 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10609 .await
10610 .unwrap();
10611
10612 cx.foreground().start_waiting();
10613 let fake_server = fake_servers.next().await.unwrap();
10614
10615 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10616 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10617 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10618
10619 let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
10620 fake_server
10621 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10622 assert_eq!(
10623 params.text_document.uri,
10624 lsp::Url::from_file_path("/file.rs").unwrap()
10625 );
10626 assert_eq!(params.options.tab_size, 4);
10627 Ok(Some(vec![lsp::TextEdit::new(
10628 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10629 ", ".to_string(),
10630 )]))
10631 })
10632 .next()
10633 .await;
10634 cx.foreground().start_waiting();
10635 format.await.unwrap();
10636 assert_eq!(
10637 editor.read_with(cx, |editor, cx| editor.text(cx)),
10638 "one, two\nthree\n"
10639 );
10640
10641 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10642 // Ensure we don't lock if formatting hangs.
10643 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10644 assert_eq!(
10645 params.text_document.uri,
10646 lsp::Url::from_file_path("/file.rs").unwrap()
10647 );
10648 futures::future::pending::<()>().await;
10649 unreachable!()
10650 });
10651 let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
10652 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10653 cx.foreground().start_waiting();
10654 format.await.unwrap();
10655 assert_eq!(
10656 editor.read_with(cx, |editor, cx| editor.text(cx)),
10657 "one\ntwo\nthree\n"
10658 );
10659 }
10660
10661 #[gpui::test]
10662 async fn test_completion(cx: &mut gpui::TestAppContext) {
10663 let mut cx = EditorLspTestContext::new_rust(
10664 lsp::ServerCapabilities {
10665 completion_provider: Some(lsp::CompletionOptions {
10666 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10667 ..Default::default()
10668 }),
10669 ..Default::default()
10670 },
10671 cx,
10672 )
10673 .await;
10674
10675 cx.set_state(indoc! {"
10676 oneˇ
10677 two
10678 three
10679 "});
10680 cx.simulate_keystroke(".");
10681 handle_completion_request(
10682 &mut cx,
10683 indoc! {"
10684 one.|<>
10685 two
10686 three
10687 "},
10688 vec!["first_completion", "second_completion"],
10689 )
10690 .await;
10691 cx.condition(|editor, _| editor.context_menu_visible())
10692 .await;
10693 let apply_additional_edits = cx.update_editor(|editor, cx| {
10694 editor.move_down(&MoveDown, cx);
10695 editor
10696 .confirm_completion(&ConfirmCompletion::default(), cx)
10697 .unwrap()
10698 });
10699 cx.assert_editor_state(indoc! {"
10700 one.second_completionˇ
10701 two
10702 three
10703 "});
10704
10705 handle_resolve_completion_request(
10706 &mut cx,
10707 Some((
10708 indoc! {"
10709 one.second_completion
10710 two
10711 threeˇ
10712 "},
10713 "\nadditional edit",
10714 )),
10715 )
10716 .await;
10717 apply_additional_edits.await.unwrap();
10718 cx.assert_editor_state(indoc! {"
10719 one.second_completionˇ
10720 two
10721 three
10722 additional edit
10723 "});
10724
10725 cx.set_state(indoc! {"
10726 one.second_completion
10727 twoˇ
10728 threeˇ
10729 additional edit
10730 "});
10731 cx.simulate_keystroke(" ");
10732 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10733 cx.simulate_keystroke("s");
10734 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10735
10736 cx.assert_editor_state(indoc! {"
10737 one.second_completion
10738 two sˇ
10739 three sˇ
10740 additional edit
10741 "});
10742 //
10743 handle_completion_request(
10744 &mut cx,
10745 indoc! {"
10746 one.second_completion
10747 two s
10748 three <s|>
10749 additional edit
10750 "},
10751 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10752 )
10753 .await;
10754 cx.condition(|editor, _| editor.context_menu_visible())
10755 .await;
10756
10757 cx.simulate_keystroke("i");
10758
10759 handle_completion_request(
10760 &mut cx,
10761 indoc! {"
10762 one.second_completion
10763 two si
10764 three <si|>
10765 additional edit
10766 "},
10767 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10768 )
10769 .await;
10770 cx.condition(|editor, _| editor.context_menu_visible())
10771 .await;
10772
10773 let apply_additional_edits = cx.update_editor(|editor, cx| {
10774 editor
10775 .confirm_completion(&ConfirmCompletion::default(), cx)
10776 .unwrap()
10777 });
10778 cx.assert_editor_state(indoc! {"
10779 one.second_completion
10780 two sixth_completionˇ
10781 three sixth_completionˇ
10782 additional edit
10783 "});
10784
10785 handle_resolve_completion_request(&mut cx, None).await;
10786 apply_additional_edits.await.unwrap();
10787
10788 cx.update(|cx| {
10789 cx.update_global::<Settings, _, _>(|settings, _| {
10790 settings.show_completions_on_input = false;
10791 })
10792 });
10793 cx.set_state("editorˇ");
10794 cx.simulate_keystroke(".");
10795 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10796 cx.simulate_keystroke("c");
10797 cx.simulate_keystroke("l");
10798 cx.simulate_keystroke("o");
10799 cx.assert_editor_state("editor.cloˇ");
10800 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10801 cx.update_editor(|editor, cx| {
10802 editor.show_completions(&ShowCompletions, cx);
10803 });
10804 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10805 cx.condition(|editor, _| editor.context_menu_visible())
10806 .await;
10807 let apply_additional_edits = cx.update_editor(|editor, cx| {
10808 editor
10809 .confirm_completion(&ConfirmCompletion::default(), cx)
10810 .unwrap()
10811 });
10812 cx.assert_editor_state("editor.closeˇ");
10813 handle_resolve_completion_request(&mut cx, None).await;
10814 apply_additional_edits.await.unwrap();
10815
10816 // Handle completion request passing a marked string specifying where the completion
10817 // should be triggered from using '|' character, what range should be replaced, and what completions
10818 // should be returned using '<' and '>' to delimit the range
10819 async fn handle_completion_request<'a>(
10820 cx: &mut EditorLspTestContext<'a>,
10821 marked_string: &str,
10822 completions: Vec<&'static str>,
10823 ) {
10824 let complete_from_marker: TextRangeMarker = '|'.into();
10825 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10826 let (_, mut marked_ranges) = marked_text_ranges_by(
10827 marked_string,
10828 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10829 );
10830
10831 let complete_from_position =
10832 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10833 let replace_range =
10834 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10835
10836 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10837 let completions = completions.clone();
10838 async move {
10839 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10840 assert_eq!(
10841 params.text_document_position.position,
10842 complete_from_position
10843 );
10844 Ok(Some(lsp::CompletionResponse::Array(
10845 completions
10846 .iter()
10847 .map(|completion_text| lsp::CompletionItem {
10848 label: completion_text.to_string(),
10849 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10850 range: replace_range,
10851 new_text: completion_text.to_string(),
10852 })),
10853 ..Default::default()
10854 })
10855 .collect(),
10856 )))
10857 }
10858 })
10859 .next()
10860 .await;
10861 }
10862
10863 async fn handle_resolve_completion_request<'a>(
10864 cx: &mut EditorLspTestContext<'a>,
10865 edit: Option<(&'static str, &'static str)>,
10866 ) {
10867 let edit = edit.map(|(marked_string, new_text)| {
10868 let (_, marked_ranges) = marked_text_ranges(marked_string, false);
10869 let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
10870 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10871 });
10872
10873 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10874 let edit = edit.clone();
10875 async move {
10876 Ok(lsp::CompletionItem {
10877 additional_text_edits: edit,
10878 ..Default::default()
10879 })
10880 }
10881 })
10882 .next()
10883 .await;
10884 }
10885 }
10886
10887 #[gpui::test]
10888 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10889 cx.update(|cx| cx.set_global(Settings::test(cx)));
10890 let language = Arc::new(Language::new(
10891 LanguageConfig {
10892 line_comment: Some("// ".into()),
10893 ..Default::default()
10894 },
10895 Some(tree_sitter_rust::language()),
10896 ));
10897
10898 let text = "
10899 fn a() {
10900 //b();
10901 // c();
10902 // d();
10903 }
10904 "
10905 .unindent();
10906
10907 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10908 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10909 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10910
10911 view.update(cx, |editor, cx| {
10912 // If multiple selections intersect a line, the line is only
10913 // toggled once.
10914 editor.change_selections(None, cx, |s| {
10915 s.select_display_ranges([
10916 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10917 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10918 ])
10919 });
10920 editor.toggle_comments(&ToggleComments, cx);
10921 assert_eq!(
10922 editor.text(cx),
10923 "
10924 fn a() {
10925 b();
10926 c();
10927 d();
10928 }
10929 "
10930 .unindent()
10931 );
10932
10933 // The comment prefix is inserted at the same column for every line
10934 // in a selection.
10935 editor.change_selections(None, cx, |s| {
10936 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10937 });
10938 editor.toggle_comments(&ToggleComments, cx);
10939 assert_eq!(
10940 editor.text(cx),
10941 "
10942 fn a() {
10943 // b();
10944 // c();
10945 // d();
10946 }
10947 "
10948 .unindent()
10949 );
10950
10951 // If a selection ends at the beginning of a line, that line is not toggled.
10952 editor.change_selections(None, cx, |s| {
10953 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10954 });
10955 editor.toggle_comments(&ToggleComments, cx);
10956 assert_eq!(
10957 editor.text(cx),
10958 "
10959 fn a() {
10960 // b();
10961 c();
10962 // d();
10963 }
10964 "
10965 .unindent()
10966 );
10967 });
10968 }
10969
10970 #[gpui::test]
10971 async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
10972 let mut cx = EditorTestContext::new(cx);
10973
10974 let html_language = Arc::new(
10975 Language::new(
10976 LanguageConfig {
10977 name: "HTML".into(),
10978 block_comment: Some(("<!-- ".into(), " -->".into())),
10979 ..Default::default()
10980 },
10981 Some(tree_sitter_html::language()),
10982 )
10983 .with_injection_query(
10984 r#"
10985 (script_element
10986 (raw_text) @content
10987 (#set! "language" "javascript"))
10988 "#,
10989 )
10990 .unwrap(),
10991 );
10992
10993 let javascript_language = Arc::new(Language::new(
10994 LanguageConfig {
10995 name: "JavaScript".into(),
10996 line_comment: Some("// ".into()),
10997 ..Default::default()
10998 },
10999 Some(tree_sitter_javascript::language()),
11000 ));
11001
11002 let registry = Arc::new(LanguageRegistry::test());
11003 registry.add(html_language.clone());
11004 registry.add(javascript_language.clone());
11005
11006 cx.update_buffer(|buffer, cx| {
11007 buffer.set_language_registry(registry);
11008 buffer.set_language(Some(html_language), cx);
11009 });
11010
11011 // Toggle comments for empty selections
11012 cx.set_state(
11013 &r#"
11014 <p>A</p>ˇ
11015 <p>B</p>ˇ
11016 <p>C</p>ˇ
11017 "#
11018 .unindent(),
11019 );
11020 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11021 cx.assert_editor_state(
11022 &r#"
11023 <!-- <p>A</p>ˇ -->
11024 <!-- <p>B</p>ˇ -->
11025 <!-- <p>C</p>ˇ -->
11026 "#
11027 .unindent(),
11028 );
11029 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11030 cx.assert_editor_state(
11031 &r#"
11032 <p>A</p>ˇ
11033 <p>B</p>ˇ
11034 <p>C</p>ˇ
11035 "#
11036 .unindent(),
11037 );
11038
11039 // Toggle comments for mixture of empty and non-empty selections, where
11040 // multiple selections occupy a given line.
11041 cx.set_state(
11042 &r#"
11043 <p>A«</p>
11044 <p>ˇ»B</p>ˇ
11045 <p>C«</p>
11046 <p>ˇ»D</p>ˇ
11047 "#
11048 .unindent(),
11049 );
11050
11051 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11052 cx.assert_editor_state(
11053 &r#"
11054 <!-- <p>A«</p>
11055 <p>ˇ»B</p>ˇ -->
11056 <!-- <p>C«</p>
11057 <p>ˇ»D</p>ˇ -->
11058 "#
11059 .unindent(),
11060 );
11061 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11062 cx.assert_editor_state(
11063 &r#"
11064 <p>A«</p>
11065 <p>ˇ»B</p>ˇ
11066 <p>C«</p>
11067 <p>ˇ»D</p>ˇ
11068 "#
11069 .unindent(),
11070 );
11071
11072 // Toggle comments when different languages are active for different
11073 // selections.
11074 cx.set_state(
11075 &r#"
11076 ˇ<script>
11077 ˇvar x = new Y();
11078 ˇ</script>
11079 "#
11080 .unindent(),
11081 );
11082 cx.foreground().run_until_parked();
11083 cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
11084 cx.assert_editor_state(
11085 &r#"
11086 <!-- ˇ<script> -->
11087 // ˇvar x = new Y();
11088 <!-- ˇ</script> -->
11089 "#
11090 .unindent(),
11091 );
11092 }
11093
11094 #[gpui::test]
11095 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
11096 cx.set_global(Settings::test(cx));
11097 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11098 let multibuffer = cx.add_model(|cx| {
11099 let mut multibuffer = MultiBuffer::new(0);
11100 multibuffer.push_excerpts(
11101 buffer.clone(),
11102 [
11103 ExcerptRange {
11104 context: Point::new(0, 0)..Point::new(0, 4),
11105 primary: None,
11106 },
11107 ExcerptRange {
11108 context: Point::new(1, 0)..Point::new(1, 4),
11109 primary: None,
11110 },
11111 ],
11112 cx,
11113 );
11114 multibuffer
11115 });
11116
11117 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
11118
11119 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
11120 view.update(cx, |view, cx| {
11121 assert_eq!(view.text(cx), "aaaa\nbbbb");
11122 view.change_selections(None, cx, |s| {
11123 s.select_ranges([
11124 Point::new(0, 0)..Point::new(0, 0),
11125 Point::new(1, 0)..Point::new(1, 0),
11126 ])
11127 });
11128
11129 view.handle_input("X", cx);
11130 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
11131 assert_eq!(
11132 view.selections.ranges(cx),
11133 [
11134 Point::new(0, 1)..Point::new(0, 1),
11135 Point::new(1, 1)..Point::new(1, 1),
11136 ]
11137 )
11138 });
11139 }
11140
11141 #[gpui::test]
11142 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
11143 cx.set_global(Settings::test(cx));
11144 let markers = vec![('[', ']').into(), ('(', ')').into()];
11145 let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11146 indoc! {"
11147 [aaaa
11148 (bbbb]
11149 cccc)",
11150 },
11151 markers.clone(),
11152 );
11153 let excerpt_ranges = markers.into_iter().map(|marker| {
11154 let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11155 ExcerptRange {
11156 context,
11157 primary: None,
11158 }
11159 });
11160 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
11161 let multibuffer = cx.add_model(|cx| {
11162 let mut multibuffer = MultiBuffer::new(0);
11163 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11164 multibuffer
11165 });
11166
11167 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
11168 view.update(cx, |view, cx| {
11169 let (expected_text, selection_ranges) = marked_text_ranges(
11170 indoc! {"
11171 aaaa
11172 bˇbbb
11173 bˇbbˇb
11174 cccc"
11175 },
11176 true,
11177 );
11178 assert_eq!(view.text(cx), expected_text);
11179 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
11180
11181 view.handle_input("X", cx);
11182
11183 let (expected_text, expected_selections) = marked_text_ranges(
11184 indoc! {"
11185 aaaa
11186 bXˇbbXb
11187 bXˇbbXˇb
11188 cccc"
11189 },
11190 false,
11191 );
11192 assert_eq!(view.text(cx), expected_text);
11193 assert_eq!(view.selections.ranges(cx), expected_selections);
11194
11195 view.newline(&Newline, cx);
11196 let (expected_text, expected_selections) = marked_text_ranges(
11197 indoc! {"
11198 aaaa
11199 bX
11200 ˇbbX
11201 b
11202 bX
11203 ˇbbX
11204 ˇb
11205 cccc"
11206 },
11207 false,
11208 );
11209 assert_eq!(view.text(cx), expected_text);
11210 assert_eq!(view.selections.ranges(cx), expected_selections);
11211 });
11212 }
11213
11214 #[gpui::test]
11215 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
11216 cx.set_global(Settings::test(cx));
11217 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11218 let mut excerpt1_id = None;
11219 let multibuffer = cx.add_model(|cx| {
11220 let mut multibuffer = MultiBuffer::new(0);
11221 excerpt1_id = multibuffer
11222 .push_excerpts(
11223 buffer.clone(),
11224 [
11225 ExcerptRange {
11226 context: Point::new(0, 0)..Point::new(1, 4),
11227 primary: None,
11228 },
11229 ExcerptRange {
11230 context: Point::new(1, 0)..Point::new(2, 4),
11231 primary: None,
11232 },
11233 ],
11234 cx,
11235 )
11236 .into_iter()
11237 .next();
11238 multibuffer
11239 });
11240 assert_eq!(
11241 multibuffer.read(cx).read(cx).text(),
11242 "aaaa\nbbbb\nbbbb\ncccc"
11243 );
11244 let (_, editor) = cx.add_window(Default::default(), |cx| {
11245 let mut editor = build_editor(multibuffer.clone(), cx);
11246 let snapshot = editor.snapshot(cx);
11247 editor.change_selections(None, cx, |s| {
11248 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11249 });
11250 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
11251 assert_eq!(
11252 editor.selections.ranges(cx),
11253 [
11254 Point::new(1, 3)..Point::new(1, 3),
11255 Point::new(2, 1)..Point::new(2, 1),
11256 ]
11257 );
11258 editor
11259 });
11260
11261 // Refreshing selections is a no-op when excerpts haven't changed.
11262 editor.update(cx, |editor, cx| {
11263 editor.change_selections(None, cx, |s| {
11264 s.refresh();
11265 });
11266 assert_eq!(
11267 editor.selections.ranges(cx),
11268 [
11269 Point::new(1, 3)..Point::new(1, 3),
11270 Point::new(2, 1)..Point::new(2, 1),
11271 ]
11272 );
11273 });
11274
11275 multibuffer.update(cx, |multibuffer, cx| {
11276 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
11277 });
11278 editor.update(cx, |editor, cx| {
11279 // Removing an excerpt causes the first selection to become degenerate.
11280 assert_eq!(
11281 editor.selections.ranges(cx),
11282 [
11283 Point::new(0, 0)..Point::new(0, 0),
11284 Point::new(0, 1)..Point::new(0, 1)
11285 ]
11286 );
11287
11288 // Refreshing selections will relocate the first selection to the original buffer
11289 // location.
11290 editor.change_selections(None, cx, |s| {
11291 s.refresh();
11292 });
11293 assert_eq!(
11294 editor.selections.ranges(cx),
11295 [
11296 Point::new(0, 1)..Point::new(0, 1),
11297 Point::new(0, 3)..Point::new(0, 3)
11298 ]
11299 );
11300 assert!(editor.selections.pending_anchor().is_some());
11301 });
11302 }
11303
11304 #[gpui::test]
11305 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
11306 cx.set_global(Settings::test(cx));
11307 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
11308 let mut excerpt1_id = None;
11309 let multibuffer = cx.add_model(|cx| {
11310 let mut multibuffer = MultiBuffer::new(0);
11311 excerpt1_id = multibuffer
11312 .push_excerpts(
11313 buffer.clone(),
11314 [
11315 ExcerptRange {
11316 context: Point::new(0, 0)..Point::new(1, 4),
11317 primary: None,
11318 },
11319 ExcerptRange {
11320 context: Point::new(1, 0)..Point::new(2, 4),
11321 primary: None,
11322 },
11323 ],
11324 cx,
11325 )
11326 .into_iter()
11327 .next();
11328 multibuffer
11329 });
11330 assert_eq!(
11331 multibuffer.read(cx).read(cx).text(),
11332 "aaaa\nbbbb\nbbbb\ncccc"
11333 );
11334 let (_, editor) = cx.add_window(Default::default(), |cx| {
11335 let mut editor = build_editor(multibuffer.clone(), cx);
11336 let snapshot = editor.snapshot(cx);
11337 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
11338 assert_eq!(
11339 editor.selections.ranges(cx),
11340 [Point::new(1, 3)..Point::new(1, 3)]
11341 );
11342 editor
11343 });
11344
11345 multibuffer.update(cx, |multibuffer, cx| {
11346 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
11347 });
11348 editor.update(cx, |editor, cx| {
11349 assert_eq!(
11350 editor.selections.ranges(cx),
11351 [Point::new(0, 0)..Point::new(0, 0)]
11352 );
11353
11354 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11355 editor.change_selections(None, cx, |s| {
11356 s.refresh();
11357 });
11358 assert_eq!(
11359 editor.selections.ranges(cx),
11360 [Point::new(0, 3)..Point::new(0, 3)]
11361 );
11362 assert!(editor.selections.pending_anchor().is_some());
11363 });
11364 }
11365
11366 #[gpui::test]
11367 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
11368 cx.update(|cx| cx.set_global(Settings::test(cx)));
11369 let language = Arc::new(
11370 Language::new(
11371 LanguageConfig {
11372 brackets: vec![
11373 BracketPair {
11374 start: "{".to_string(),
11375 end: "}".to_string(),
11376 close: true,
11377 newline: true,
11378 },
11379 BracketPair {
11380 start: "/* ".to_string(),
11381 end: " */".to_string(),
11382 close: true,
11383 newline: true,
11384 },
11385 ],
11386 ..Default::default()
11387 },
11388 Some(tree_sitter_rust::language()),
11389 )
11390 .with_indents_query("")
11391 .unwrap(),
11392 );
11393
11394 let text = concat!(
11395 "{ }\n", // Suppress rustfmt
11396 " x\n", //
11397 " /* */\n", //
11398 "x\n", //
11399 "{{} }\n", //
11400 );
11401
11402 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
11403 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
11404 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
11405 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
11406 .await;
11407
11408 view.update(cx, |view, cx| {
11409 view.change_selections(None, cx, |s| {
11410 s.select_display_ranges([
11411 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
11412 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
11413 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
11414 ])
11415 });
11416 view.newline(&Newline, cx);
11417
11418 assert_eq!(
11419 view.buffer().read(cx).read(cx).text(),
11420 concat!(
11421 "{ \n", // Suppress rustfmt
11422 "\n", //
11423 "}\n", //
11424 " x\n", //
11425 " /* \n", //
11426 " \n", //
11427 " */\n", //
11428 "x\n", //
11429 "{{} \n", //
11430 "}\n", //
11431 )
11432 );
11433 });
11434 }
11435
11436 #[gpui::test]
11437 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
11438 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11439
11440 cx.set_global(Settings::test(cx));
11441 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11442
11443 editor.update(cx, |editor, cx| {
11444 struct Type1;
11445 struct Type2;
11446
11447 let buffer = buffer.read(cx).snapshot(cx);
11448
11449 let anchor_range = |range: Range<Point>| {
11450 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
11451 };
11452
11453 editor.highlight_background::<Type1>(
11454 vec![
11455 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11456 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11457 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11458 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11459 ],
11460 |_| Color::red(),
11461 cx,
11462 );
11463 editor.highlight_background::<Type2>(
11464 vec![
11465 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11466 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11467 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11468 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11469 ],
11470 |_| Color::green(),
11471 cx,
11472 );
11473
11474 let snapshot = editor.snapshot(cx);
11475 let mut highlighted_ranges = editor.background_highlights_in_range(
11476 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11477 &snapshot,
11478 cx.global::<Settings>().theme.as_ref(),
11479 );
11480 // Enforce a consistent ordering based on color without relying on the ordering of the
11481 // highlight's `TypeId` which is non-deterministic.
11482 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11483 assert_eq!(
11484 highlighted_ranges,
11485 &[
11486 (
11487 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
11488 Color::green(),
11489 ),
11490 (
11491 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
11492 Color::green(),
11493 ),
11494 (
11495 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
11496 Color::red(),
11497 ),
11498 (
11499 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11500 Color::red(),
11501 ),
11502 ]
11503 );
11504 assert_eq!(
11505 editor.background_highlights_in_range(
11506 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11507 &snapshot,
11508 cx.global::<Settings>().theme.as_ref(),
11509 ),
11510 &[(
11511 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11512 Color::red(),
11513 )]
11514 );
11515 });
11516 }
11517
11518 #[gpui::test]
11519 fn test_following(cx: &mut gpui::MutableAppContext) {
11520 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11521
11522 cx.set_global(Settings::test(cx));
11523
11524 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11525 let (_, follower) = cx.add_window(
11526 WindowOptions {
11527 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
11528 ..Default::default()
11529 },
11530 |cx| build_editor(buffer.clone(), cx),
11531 );
11532
11533 let pending_update = Rc::new(RefCell::new(None));
11534 follower.update(cx, {
11535 let update = pending_update.clone();
11536 |_, cx| {
11537 cx.subscribe(&leader, move |_, leader, event, cx| {
11538 leader
11539 .read(cx)
11540 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
11541 })
11542 .detach();
11543 }
11544 });
11545
11546 // Update the selections only
11547 leader.update(cx, |leader, cx| {
11548 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11549 });
11550 follower.update(cx, |follower, cx| {
11551 follower
11552 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11553 .unwrap();
11554 });
11555 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
11556
11557 // Update the scroll position only
11558 leader.update(cx, |leader, cx| {
11559 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11560 });
11561 follower.update(cx, |follower, cx| {
11562 follower
11563 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11564 .unwrap();
11565 });
11566 assert_eq!(
11567 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
11568 vec2f(1.5, 3.5)
11569 );
11570
11571 // Update the selections and scroll position
11572 leader.update(cx, |leader, cx| {
11573 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
11574 leader.request_autoscroll(Autoscroll::Newest, cx);
11575 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11576 });
11577 follower.update(cx, |follower, cx| {
11578 let initial_scroll_position = follower.scroll_position(cx);
11579 follower
11580 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11581 .unwrap();
11582 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
11583 assert!(follower.autoscroll_request.is_some());
11584 });
11585 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
11586
11587 // Creating a pending selection that precedes another selection
11588 leader.update(cx, |leader, cx| {
11589 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11590 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
11591 });
11592 follower.update(cx, |follower, cx| {
11593 follower
11594 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11595 .unwrap();
11596 });
11597 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
11598
11599 // Extend the pending selection so that it surrounds another selection
11600 leader.update(cx, |leader, cx| {
11601 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
11602 });
11603 follower.update(cx, |follower, cx| {
11604 follower
11605 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11606 .unwrap();
11607 });
11608 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
11609 }
11610
11611 #[test]
11612 fn test_combine_syntax_and_fuzzy_match_highlights() {
11613 let string = "abcdefghijklmnop";
11614 let syntax_ranges = [
11615 (
11616 0..3,
11617 HighlightStyle {
11618 color: Some(Color::red()),
11619 ..Default::default()
11620 },
11621 ),
11622 (
11623 4..8,
11624 HighlightStyle {
11625 color: Some(Color::green()),
11626 ..Default::default()
11627 },
11628 ),
11629 ];
11630 let match_indices = [4, 6, 7, 8];
11631 assert_eq!(
11632 combine_syntax_and_fuzzy_match_highlights(
11633 string,
11634 Default::default(),
11635 syntax_ranges.into_iter(),
11636 &match_indices,
11637 ),
11638 &[
11639 (
11640 0..3,
11641 HighlightStyle {
11642 color: Some(Color::red()),
11643 ..Default::default()
11644 },
11645 ),
11646 (
11647 4..5,
11648 HighlightStyle {
11649 color: Some(Color::green()),
11650 weight: Some(fonts::Weight::BOLD),
11651 ..Default::default()
11652 },
11653 ),
11654 (
11655 5..6,
11656 HighlightStyle {
11657 color: Some(Color::green()),
11658 ..Default::default()
11659 },
11660 ),
11661 (
11662 6..8,
11663 HighlightStyle {
11664 color: Some(Color::green()),
11665 weight: Some(fonts::Weight::BOLD),
11666 ..Default::default()
11667 },
11668 ),
11669 (
11670 8..9,
11671 HighlightStyle {
11672 weight: Some(fonts::Weight::BOLD),
11673 ..Default::default()
11674 },
11675 ),
11676 ]
11677 );
11678 }
11679
11680 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11681 let point = DisplayPoint::new(row as u32, column as u32);
11682 point..point
11683 }
11684
11685 fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11686 let (text, ranges) = marked_text_ranges(marked_text, true);
11687 assert_eq!(view.text(cx), text);
11688 assert_eq!(
11689 view.selections.ranges(cx),
11690 ranges,
11691 "Assert selections are {}",
11692 marked_text
11693 );
11694 }
11695}
11696
11697trait RangeExt<T> {
11698 fn sorted(&self) -> Range<T>;
11699 fn to_inclusive(&self) -> RangeInclusive<T>;
11700}
11701
11702impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11703 fn sorted(&self) -> Self {
11704 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11705 }
11706
11707 fn to_inclusive(&self) -> RangeInclusive<T> {
11708 self.start.clone()..=self.end.clone()
11709 }
11710}
11711
11712trait RangeToAnchorExt {
11713 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11714}
11715
11716impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11717 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11718 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11719 }
11720}