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