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