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