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