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