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