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