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