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