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