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
5744impl EditorSnapshot {
5745 pub fn is_focused(&self) -> bool {
5746 self.is_focused
5747 }
5748
5749 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5750 self.placeholder_text.as_ref()
5751 }
5752
5753 pub fn scroll_position(&self) -> Vector2F {
5754 compute_scroll_position(
5755 &self.display_snapshot,
5756 self.scroll_position,
5757 &self.scroll_top_anchor,
5758 )
5759 }
5760}
5761
5762impl Deref for EditorSnapshot {
5763 type Target = DisplaySnapshot;
5764
5765 fn deref(&self) -> &Self::Target {
5766 &self.display_snapshot
5767 }
5768}
5769
5770fn compute_scroll_position(
5771 snapshot: &DisplaySnapshot,
5772 mut scroll_position: Vector2F,
5773 scroll_top_anchor: &Anchor,
5774) -> Vector2F {
5775 if *scroll_top_anchor != Anchor::min() {
5776 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5777 scroll_position.set_y(scroll_top + scroll_position.y());
5778 } else {
5779 scroll_position.set_y(0.);
5780 }
5781 scroll_position
5782}
5783
5784#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5785pub enum Event {
5786 Activate,
5787 BufferEdited,
5788 Edited,
5789 Reparsed,
5790 Blurred,
5791 DirtyChanged,
5792 Saved,
5793 TitleChanged,
5794 SelectionsChanged { local: bool },
5795 ScrollPositionChanged { local: bool },
5796 Closed,
5797 IgnoredInput,
5798}
5799
5800pub struct EditorFocused(pub ViewHandle<Editor>);
5801pub struct EditorBlurred(pub ViewHandle<Editor>);
5802pub struct EditorReleased(pub WeakViewHandle<Editor>);
5803
5804impl Entity for Editor {
5805 type Event = Event;
5806
5807 fn release(&mut self, cx: &mut MutableAppContext) {
5808 cx.emit_global(EditorReleased(self.handle.clone()));
5809 }
5810}
5811
5812impl View for Editor {
5813 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5814 let style = self.style(cx);
5815 let font_changed = self.display_map.update(cx, |map, cx| {
5816 map.set_font(style.text.font_id, style.text.font_size, cx)
5817 });
5818
5819 if font_changed {
5820 let handle = self.handle.clone();
5821 cx.defer(move |cx| {
5822 if let Some(editor) = handle.upgrade(cx) {
5823 editor.update(cx, |editor, cx| {
5824 hide_hover(editor, cx);
5825 })
5826 }
5827 });
5828 }
5829
5830 Stack::new()
5831 .with_child(
5832 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
5833 )
5834 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
5835 .boxed()
5836 }
5837
5838 fn ui_name() -> &'static str {
5839 "Editor"
5840 }
5841
5842 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5843 let focused_event = EditorFocused(cx.handle());
5844 cx.emit_global(focused_event);
5845 if let Some(rename) = self.pending_rename.as_ref() {
5846 cx.focus(&rename.editor);
5847 } else {
5848 self.focused = true;
5849 self.blink_cursors(self.blink_epoch, cx);
5850 self.buffer.update(cx, |buffer, cx| {
5851 buffer.finalize_last_transaction(cx);
5852 if self.leader_replica_id.is_none() {
5853 buffer.set_active_selections(
5854 &self.selections.disjoint_anchors(),
5855 self.selections.line_mode,
5856 cx,
5857 );
5858 }
5859 });
5860 }
5861 }
5862
5863 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5864 let blurred_event = EditorBlurred(cx.handle());
5865 cx.emit_global(blurred_event);
5866 self.focused = false;
5867 self.buffer
5868 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5869 self.hide_context_menu(cx);
5870 hide_hover(self, cx);
5871 cx.emit(Event::Blurred);
5872 cx.notify();
5873 }
5874
5875 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5876 let mut context = Self::default_keymap_context();
5877 let mode = match self.mode {
5878 EditorMode::SingleLine => "single_line",
5879 EditorMode::AutoHeight { .. } => "auto_height",
5880 EditorMode::Full => "full",
5881 };
5882 context.map.insert("mode".into(), mode.into());
5883 if self.pending_rename.is_some() {
5884 context.set.insert("renaming".into());
5885 }
5886 match self.context_menu.as_ref() {
5887 Some(ContextMenu::Completions(_)) => {
5888 context.set.insert("showing_completions".into());
5889 }
5890 Some(ContextMenu::CodeActions(_)) => {
5891 context.set.insert("showing_code_actions".into());
5892 }
5893 None => {}
5894 }
5895
5896 for layer in self.keymap_context_layers.values() {
5897 context.extend(layer);
5898 }
5899
5900 context
5901 }
5902
5903 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
5904 Some(
5905 self.buffer
5906 .read(cx)
5907 .read(cx)
5908 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
5909 .collect(),
5910 )
5911 }
5912
5913 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
5914 // Prevent the IME menu from appearing when holding down an alphabetic key
5915 // while input is disabled.
5916 if !self.input_enabled {
5917 return None;
5918 }
5919
5920 let range = self.selections.newest::<OffsetUtf16>(cx).range();
5921 Some(range.start.0..range.end.0)
5922 }
5923
5924 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
5925 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
5926 let snapshot = self.buffer.read(cx).read(cx);
5927 let range_utf16 =
5928 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot);
5929 Some(range_utf16.start.0..range_utf16.end.0)
5930 }
5931
5932 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
5933 self.clear_text_highlights::<InputComposition>(cx);
5934 self.ime_transaction.take();
5935 }
5936
5937 fn replace_text_in_range(
5938 &mut self,
5939 range_utf16: Option<Range<usize>>,
5940 text: &str,
5941 cx: &mut ViewContext<Self>,
5942 ) {
5943 if !self.input_enabled {
5944 cx.emit(Event::IgnoredInput);
5945 return;
5946 }
5947
5948 self.transact(cx, |this, cx| {
5949 if let Some(range) = range_utf16.or_else(|| this.marked_text_range(cx)) {
5950 this.change_selections(None, cx, |selections| {
5951 selections.select_ranges([OffsetUtf16(range.start)..OffsetUtf16(range.end)])
5952 });
5953 }
5954 this.handle_input(text, cx);
5955 });
5956
5957 if let Some(transaction) = self.ime_transaction {
5958 self.buffer.update(cx, |buffer, cx| {
5959 buffer.group_until_transaction(transaction, cx);
5960 });
5961 }
5962
5963 self.unmark_text(cx);
5964 }
5965
5966 fn replace_and_mark_text_in_range(
5967 &mut self,
5968 range_utf16: Option<Range<usize>>,
5969 text: &str,
5970 new_selected_range_utf16: Option<Range<usize>>,
5971 cx: &mut ViewContext<Self>,
5972 ) {
5973 if !self.input_enabled {
5974 cx.emit(Event::IgnoredInput);
5975 return;
5976 }
5977
5978 let transaction = self.transact(cx, |this, cx| {
5979 let range_to_replace = if let Some(mut marked_range) = this.marked_text_range(cx) {
5980 if let Some(relative_range_utf16) = range_utf16.as_ref() {
5981 marked_range.end = marked_range.start + relative_range_utf16.end;
5982 marked_range.start += relative_range_utf16.start;
5983 }
5984 Some(marked_range)
5985 } else if let Some(range_utf16) = range_utf16 {
5986 Some(range_utf16)
5987 } else {
5988 None
5989 };
5990
5991 if let Some(range) = range_to_replace {
5992 this.change_selections(None, cx, |s| {
5993 s.select_ranges([OffsetUtf16(range.start)..OffsetUtf16(range.end)])
5994 });
5995 }
5996
5997 let selection = this.selections.newest_anchor();
5998 let marked_range = {
5999 let snapshot = this.buffer.read(cx).read(cx);
6000 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6001 };
6002
6003 if text.is_empty() {
6004 this.unmark_text(cx);
6005 } else {
6006 this.highlight_text::<InputComposition>(
6007 vec![marked_range.clone()],
6008 this.style(cx).composition_mark,
6009 cx,
6010 );
6011 }
6012
6013 this.handle_input(text, cx);
6014
6015 if let Some(mut new_selected_range) = new_selected_range_utf16 {
6016 let snapshot = this.buffer.read(cx).read(cx);
6017 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6018 new_selected_range.start += insertion_start;
6019 new_selected_range.end += insertion_start;
6020 drop(snapshot);
6021 this.change_selections(None, cx, |selections| {
6022 selections
6023 .select_ranges([OffsetUtf16(new_selected_range.start)
6024 ..OffsetUtf16(new_selected_range.end)])
6025 });
6026 }
6027 });
6028
6029 self.ime_transaction = self.ime_transaction.or(transaction);
6030 if let Some(transaction) = self.ime_transaction {
6031 self.buffer.update(cx, |buffer, cx| {
6032 buffer.group_until_transaction(transaction, cx);
6033 });
6034 }
6035
6036 if self.text_highlights::<InputComposition>(cx).is_none() {
6037 self.ime_transaction.take();
6038 }
6039 }
6040}
6041
6042fn build_style(
6043 settings: &Settings,
6044 get_field_editor_theme: Option<GetFieldEditorTheme>,
6045 override_text_style: Option<&OverrideTextStyle>,
6046 cx: &AppContext,
6047) -> EditorStyle {
6048 let font_cache = cx.font_cache();
6049
6050 let mut theme = settings.theme.editor.clone();
6051 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6052 let field_editor_theme = get_field_editor_theme(&settings.theme);
6053 theme.text_color = field_editor_theme.text.color;
6054 theme.selection = field_editor_theme.selection;
6055 theme.background = field_editor_theme
6056 .container
6057 .background_color
6058 .unwrap_or_default();
6059 EditorStyle {
6060 text: field_editor_theme.text,
6061 placeholder_text: field_editor_theme.placeholder_text,
6062 theme,
6063 }
6064 } else {
6065 let font_family_id = settings.buffer_font_family;
6066 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6067 let font_properties = Default::default();
6068 let font_id = font_cache
6069 .select_font(font_family_id, &font_properties)
6070 .unwrap();
6071 let font_size = settings.buffer_font_size;
6072 EditorStyle {
6073 text: TextStyle {
6074 color: settings.theme.editor.text_color,
6075 font_family_name,
6076 font_family_id,
6077 font_id,
6078 font_size,
6079 font_properties,
6080 underline: Default::default(),
6081 },
6082 placeholder_text: None,
6083 theme,
6084 }
6085 };
6086
6087 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6088 if let Some(highlighted) = style
6089 .text
6090 .clone()
6091 .highlight(highlight_style, font_cache)
6092 .log_err()
6093 {
6094 style.text = highlighted;
6095 }
6096 }
6097
6098 style
6099}
6100
6101trait SelectionExt {
6102 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6103 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6104 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6105 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6106 -> Range<u32>;
6107}
6108
6109impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6110 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6111 let start = self.start.to_point(buffer);
6112 let end = self.end.to_point(buffer);
6113 if self.reversed {
6114 end..start
6115 } else {
6116 start..end
6117 }
6118 }
6119
6120 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6121 let start = self.start.to_offset(buffer);
6122 let end = self.end.to_offset(buffer);
6123 if self.reversed {
6124 end..start
6125 } else {
6126 start..end
6127 }
6128 }
6129
6130 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6131 let start = self
6132 .start
6133 .to_point(&map.buffer_snapshot)
6134 .to_display_point(map);
6135 let end = self
6136 .end
6137 .to_point(&map.buffer_snapshot)
6138 .to_display_point(map);
6139 if self.reversed {
6140 end..start
6141 } else {
6142 start..end
6143 }
6144 }
6145
6146 fn spanned_rows(
6147 &self,
6148 include_end_if_at_line_start: bool,
6149 map: &DisplaySnapshot,
6150 ) -> Range<u32> {
6151 let start = self.start.to_point(&map.buffer_snapshot);
6152 let mut end = self.end.to_point(&map.buffer_snapshot);
6153 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6154 end.row -= 1;
6155 }
6156
6157 let buffer_start = map.prev_line_boundary(start).0;
6158 let buffer_end = map.next_line_boundary(end).0;
6159 buffer_start.row..buffer_end.row + 1
6160 }
6161}
6162
6163impl<T: InvalidationRegion> InvalidationStack<T> {
6164 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6165 where
6166 S: Clone + ToOffset,
6167 {
6168 while let Some(region) = self.last() {
6169 let all_selections_inside_invalidation_ranges =
6170 if selections.len() == region.ranges().len() {
6171 selections
6172 .iter()
6173 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6174 .all(|(selection, invalidation_range)| {
6175 let head = selection.head().to_offset(&buffer);
6176 invalidation_range.start <= head && invalidation_range.end >= head
6177 })
6178 } else {
6179 false
6180 };
6181
6182 if all_selections_inside_invalidation_ranges {
6183 break;
6184 } else {
6185 self.pop();
6186 }
6187 }
6188 }
6189}
6190
6191impl<T> Default for InvalidationStack<T> {
6192 fn default() -> Self {
6193 Self(Default::default())
6194 }
6195}
6196
6197impl<T> Deref for InvalidationStack<T> {
6198 type Target = Vec<T>;
6199
6200 fn deref(&self) -> &Self::Target {
6201 &self.0
6202 }
6203}
6204
6205impl<T> DerefMut for InvalidationStack<T> {
6206 fn deref_mut(&mut self) -> &mut Self::Target {
6207 &mut self.0
6208 }
6209}
6210
6211impl InvalidationRegion for BracketPairState {
6212 fn ranges(&self) -> &[Range<Anchor>] {
6213 &self.ranges
6214 }
6215}
6216
6217impl InvalidationRegion for SnippetState {
6218 fn ranges(&self) -> &[Range<Anchor>] {
6219 &self.ranges[self.active_index]
6220 }
6221}
6222
6223impl Deref for EditorStyle {
6224 type Target = theme::Editor;
6225
6226 fn deref(&self) -> &Self::Target {
6227 &self.theme
6228 }
6229}
6230
6231pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6232 let mut highlighted_lines = Vec::new();
6233 for line in diagnostic.message.lines() {
6234 highlighted_lines.push(highlight_diagnostic_message(line));
6235 }
6236
6237 Arc::new(move |cx: &mut BlockContext| {
6238 let settings = cx.global::<Settings>();
6239 let theme = &settings.theme.editor;
6240 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6241 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6242 Flex::column()
6243 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6244 Label::new(
6245 line.clone(),
6246 style.message.clone().with_font_size(font_size),
6247 )
6248 .with_highlights(highlights.clone())
6249 .contained()
6250 .with_margin_left(cx.anchor_x)
6251 .boxed()
6252 }))
6253 .aligned()
6254 .left()
6255 .boxed()
6256 })
6257}
6258
6259pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6260 let mut message_without_backticks = String::new();
6261 let mut prev_offset = 0;
6262 let mut inside_block = false;
6263 let mut highlights = Vec::new();
6264 for (match_ix, (offset, _)) in message
6265 .match_indices('`')
6266 .chain([(message.len(), "")])
6267 .enumerate()
6268 {
6269 message_without_backticks.push_str(&message[prev_offset..offset]);
6270 if inside_block {
6271 highlights.extend(prev_offset - match_ix..offset - match_ix);
6272 }
6273
6274 inside_block = !inside_block;
6275 prev_offset = offset + 1;
6276 }
6277
6278 (message_without_backticks, highlights)
6279}
6280
6281pub fn diagnostic_style(
6282 severity: DiagnosticSeverity,
6283 valid: bool,
6284 theme: &theme::Editor,
6285) -> DiagnosticStyle {
6286 match (severity, valid) {
6287 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6288 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6289 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6290 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6291 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6292 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6293 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6294 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6295 _ => theme.invalid_hint_diagnostic.clone(),
6296 }
6297}
6298
6299pub fn combine_syntax_and_fuzzy_match_highlights(
6300 text: &str,
6301 default_style: HighlightStyle,
6302 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6303 match_indices: &[usize],
6304) -> Vec<(Range<usize>, HighlightStyle)> {
6305 let mut result = Vec::new();
6306 let mut match_indices = match_indices.iter().copied().peekable();
6307
6308 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6309 {
6310 syntax_highlight.weight = None;
6311
6312 // Add highlights for any fuzzy match characters before the next
6313 // syntax highlight range.
6314 while let Some(&match_index) = match_indices.peek() {
6315 if match_index >= range.start {
6316 break;
6317 }
6318 match_indices.next();
6319 let end_index = char_ix_after(match_index, text);
6320 let mut match_style = default_style;
6321 match_style.weight = Some(fonts::Weight::BOLD);
6322 result.push((match_index..end_index, match_style));
6323 }
6324
6325 if range.start == usize::MAX {
6326 break;
6327 }
6328
6329 // Add highlights for any fuzzy match characters within the
6330 // syntax highlight range.
6331 let mut offset = range.start;
6332 while let Some(&match_index) = match_indices.peek() {
6333 if match_index >= range.end {
6334 break;
6335 }
6336
6337 match_indices.next();
6338 if match_index > offset {
6339 result.push((offset..match_index, syntax_highlight));
6340 }
6341
6342 let mut end_index = char_ix_after(match_index, text);
6343 while let Some(&next_match_index) = match_indices.peek() {
6344 if next_match_index == end_index && next_match_index < range.end {
6345 end_index = char_ix_after(next_match_index, text);
6346 match_indices.next();
6347 } else {
6348 break;
6349 }
6350 }
6351
6352 let mut match_style = syntax_highlight;
6353 match_style.weight = Some(fonts::Weight::BOLD);
6354 result.push((match_index..end_index, match_style));
6355 offset = end_index;
6356 }
6357
6358 if offset < range.end {
6359 result.push((offset..range.end, syntax_highlight));
6360 }
6361 }
6362
6363 fn char_ix_after(ix: usize, text: &str) -> usize {
6364 ix + text[ix..].chars().next().unwrap().len_utf8()
6365 }
6366
6367 result
6368}
6369
6370pub fn styled_runs_for_code_label<'a>(
6371 label: &'a CodeLabel,
6372 syntax_theme: &'a theme::SyntaxTheme,
6373) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6374 let fade_out = HighlightStyle {
6375 fade_out: Some(0.35),
6376 ..Default::default()
6377 };
6378
6379 let mut prev_end = label.filter_range.end;
6380 label
6381 .runs
6382 .iter()
6383 .enumerate()
6384 .flat_map(move |(ix, (range, highlight_id))| {
6385 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6386 style
6387 } else {
6388 return Default::default();
6389 };
6390 let mut muted_style = style.clone();
6391 muted_style.highlight(fade_out);
6392
6393 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6394 if range.start >= label.filter_range.end {
6395 if range.start > prev_end {
6396 runs.push((prev_end..range.start, fade_out));
6397 }
6398 runs.push((range.clone(), muted_style));
6399 } else if range.end <= label.filter_range.end {
6400 runs.push((range.clone(), style));
6401 } else {
6402 runs.push((range.start..label.filter_range.end, style));
6403 runs.push((label.filter_range.end..range.end, muted_style));
6404 }
6405 prev_end = cmp::max(prev_end, range.end);
6406
6407 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6408 runs.push((prev_end..label.text.len(), fade_out));
6409 }
6410
6411 runs
6412 })
6413}
6414
6415#[cfg(test)]
6416mod tests {
6417 use crate::test::{
6418 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6419 EditorTestContext,
6420 };
6421
6422 use super::*;
6423 use futures::StreamExt;
6424 use gpui::{
6425 geometry::rect::RectF,
6426 platform::{WindowBounds, WindowOptions},
6427 };
6428 use indoc::indoc;
6429 use language::{FakeLspAdapter, LanguageConfig};
6430 use project::FakeFs;
6431 use settings::EditorSettings;
6432 use std::{cell::RefCell, rc::Rc, time::Instant};
6433 use text::Point;
6434 use unindent::Unindent;
6435 use util::{
6436 assert_set_eq,
6437 test::{
6438 marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker,
6439 },
6440 };
6441 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6442
6443 #[gpui::test]
6444 fn test_edit_events(cx: &mut MutableAppContext) {
6445 cx.set_global(Settings::test(cx));
6446 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6447
6448 let events = Rc::new(RefCell::new(Vec::new()));
6449 let (_, editor1) = cx.add_window(Default::default(), {
6450 let events = events.clone();
6451 |cx| {
6452 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6453 if matches!(
6454 event,
6455 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6456 ) {
6457 events.borrow_mut().push(("editor1", *event));
6458 }
6459 })
6460 .detach();
6461 Editor::for_buffer(buffer.clone(), None, cx)
6462 }
6463 });
6464 let (_, editor2) = cx.add_window(Default::default(), {
6465 let events = events.clone();
6466 |cx| {
6467 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6468 if matches!(
6469 event,
6470 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6471 ) {
6472 events.borrow_mut().push(("editor2", *event));
6473 }
6474 })
6475 .detach();
6476 Editor::for_buffer(buffer.clone(), None, cx)
6477 }
6478 });
6479 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6480
6481 // Mutating editor 1 will emit an `Edited` event only for that editor.
6482 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6483 assert_eq!(
6484 mem::take(&mut *events.borrow_mut()),
6485 [
6486 ("editor1", Event::Edited),
6487 ("editor1", Event::BufferEdited),
6488 ("editor2", Event::BufferEdited),
6489 ("editor1", Event::DirtyChanged),
6490 ("editor2", Event::DirtyChanged)
6491 ]
6492 );
6493
6494 // Mutating editor 2 will emit an `Edited` event only for that editor.
6495 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6496 assert_eq!(
6497 mem::take(&mut *events.borrow_mut()),
6498 [
6499 ("editor2", Event::Edited),
6500 ("editor1", Event::BufferEdited),
6501 ("editor2", Event::BufferEdited),
6502 ]
6503 );
6504
6505 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6506 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6507 assert_eq!(
6508 mem::take(&mut *events.borrow_mut()),
6509 [
6510 ("editor1", Event::Edited),
6511 ("editor1", Event::BufferEdited),
6512 ("editor2", Event::BufferEdited),
6513 ("editor1", Event::DirtyChanged),
6514 ("editor2", Event::DirtyChanged),
6515 ]
6516 );
6517
6518 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6519 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6520 assert_eq!(
6521 mem::take(&mut *events.borrow_mut()),
6522 [
6523 ("editor1", Event::Edited),
6524 ("editor1", Event::BufferEdited),
6525 ("editor2", Event::BufferEdited),
6526 ("editor1", Event::DirtyChanged),
6527 ("editor2", Event::DirtyChanged),
6528 ]
6529 );
6530
6531 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6532 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6533 assert_eq!(
6534 mem::take(&mut *events.borrow_mut()),
6535 [
6536 ("editor2", Event::Edited),
6537 ("editor1", Event::BufferEdited),
6538 ("editor2", Event::BufferEdited),
6539 ("editor1", Event::DirtyChanged),
6540 ("editor2", Event::DirtyChanged),
6541 ]
6542 );
6543
6544 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6545 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6546 assert_eq!(
6547 mem::take(&mut *events.borrow_mut()),
6548 [
6549 ("editor2", Event::Edited),
6550 ("editor1", Event::BufferEdited),
6551 ("editor2", Event::BufferEdited),
6552 ("editor1", Event::DirtyChanged),
6553 ("editor2", Event::DirtyChanged),
6554 ]
6555 );
6556
6557 // No event is emitted when the mutation is a no-op.
6558 editor2.update(cx, |editor, cx| {
6559 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6560
6561 editor.backspace(&Backspace, cx);
6562 });
6563 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6564 }
6565
6566 #[gpui::test]
6567 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6568 cx.set_global(Settings::test(cx));
6569 let mut now = Instant::now();
6570 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6571 let group_interval = buffer.read(cx).transaction_group_interval();
6572 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6573 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6574
6575 editor.update(cx, |editor, cx| {
6576 editor.start_transaction_at(now, cx);
6577 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6578
6579 editor.insert("cd", cx);
6580 editor.end_transaction_at(now, cx);
6581 assert_eq!(editor.text(cx), "12cd56");
6582 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6583
6584 editor.start_transaction_at(now, cx);
6585 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6586 editor.insert("e", cx);
6587 editor.end_transaction_at(now, cx);
6588 assert_eq!(editor.text(cx), "12cde6");
6589 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6590
6591 now += group_interval + Duration::from_millis(1);
6592 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6593
6594 // Simulate an edit in another editor
6595 buffer.update(cx, |buffer, cx| {
6596 buffer.start_transaction_at(now, cx);
6597 buffer.edit([(0..1, "a")], cx);
6598 buffer.edit([(1..1, "b")], cx);
6599 buffer.end_transaction_at(now, cx);
6600 });
6601
6602 assert_eq!(editor.text(cx), "ab2cde6");
6603 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6604
6605 // Last transaction happened past the group interval in a different editor.
6606 // Undo it individually and don't restore selections.
6607 editor.undo(&Undo, cx);
6608 assert_eq!(editor.text(cx), "12cde6");
6609 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6610
6611 // First two transactions happened within the group interval in this editor.
6612 // Undo them together and restore selections.
6613 editor.undo(&Undo, cx);
6614 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6615 assert_eq!(editor.text(cx), "123456");
6616 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6617
6618 // Redo the first two transactions together.
6619 editor.redo(&Redo, cx);
6620 assert_eq!(editor.text(cx), "12cde6");
6621 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6622
6623 // Redo the last transaction on its own.
6624 editor.redo(&Redo, cx);
6625 assert_eq!(editor.text(cx), "ab2cde6");
6626 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6627
6628 // Test empty transactions.
6629 editor.start_transaction_at(now, cx);
6630 editor.end_transaction_at(now, cx);
6631 editor.undo(&Undo, cx);
6632 assert_eq!(editor.text(cx), "12cde6");
6633 });
6634 }
6635
6636 #[gpui::test]
6637 fn test_ime_composition(cx: &mut MutableAppContext) {
6638 cx.set_global(Settings::test(cx));
6639 let buffer = cx.add_model(|cx| {
6640 let mut buffer = language::Buffer::new(0, "abcde", cx);
6641 // Ensure automatic grouping doesn't occur.
6642 buffer.set_group_interval(Duration::ZERO);
6643 buffer
6644 });
6645
6646 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6647 cx.add_window(Default::default(), |cx| {
6648 let mut editor = build_editor(buffer.clone(), cx);
6649
6650 // Start a new IME composition.
6651 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6652 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
6653 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
6654 assert_eq!(editor.text(cx), "äbcde");
6655 assert_eq!(editor.marked_text_range(cx), Some(0..1));
6656
6657 // Finalize IME composition.
6658 editor.replace_text_in_range(Some(0..1), "ā", cx);
6659 assert_eq!(editor.text(cx), "ābcde");
6660 assert_eq!(editor.marked_text_range(cx), None);
6661
6662 // IME composition edits are grouped and are undone/redone at once.
6663 editor.undo(&Default::default(), cx);
6664 assert_eq!(editor.text(cx), "abcde");
6665 assert_eq!(editor.marked_text_range(cx), None);
6666 editor.redo(&Default::default(), cx);
6667 assert_eq!(editor.text(cx), "ābcde");
6668 assert_eq!(editor.marked_text_range(cx), None);
6669
6670 // Start a new IME composition.
6671 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6672 assert_eq!(editor.marked_text_range(cx), Some(0..1));
6673
6674 // Undoing during an IME composition cancels it.
6675 editor.undo(&Default::default(), cx);
6676 assert_eq!(editor.text(cx), "ābcde");
6677 assert_eq!(editor.marked_text_range(cx), None);
6678
6679 editor
6680 });
6681 }
6682
6683 #[gpui::test]
6684 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6685 cx.set_global(Settings::test(cx));
6686
6687 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6688 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6689 editor.update(cx, |view, cx| {
6690 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6691 });
6692 assert_eq!(
6693 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6694 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6695 );
6696
6697 editor.update(cx, |view, cx| {
6698 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6699 });
6700
6701 assert_eq!(
6702 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6703 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6704 );
6705
6706 editor.update(cx, |view, cx| {
6707 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6708 });
6709
6710 assert_eq!(
6711 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6712 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6713 );
6714
6715 editor.update(cx, |view, cx| {
6716 view.end_selection(cx);
6717 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6718 });
6719
6720 assert_eq!(
6721 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6722 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6723 );
6724
6725 editor.update(cx, |view, cx| {
6726 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6727 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6728 });
6729
6730 assert_eq!(
6731 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6732 [
6733 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6734 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6735 ]
6736 );
6737
6738 editor.update(cx, |view, cx| {
6739 view.end_selection(cx);
6740 });
6741
6742 assert_eq!(
6743 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6744 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6745 );
6746 }
6747
6748 #[gpui::test]
6749 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6750 cx.set_global(Settings::test(cx));
6751 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6752 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6753
6754 view.update(cx, |view, cx| {
6755 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6756 assert_eq!(
6757 view.selections.display_ranges(cx),
6758 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6759 );
6760 });
6761
6762 view.update(cx, |view, cx| {
6763 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6764 assert_eq!(
6765 view.selections.display_ranges(cx),
6766 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6767 );
6768 });
6769
6770 view.update(cx, |view, cx| {
6771 view.cancel(&Cancel, cx);
6772 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6773 assert_eq!(
6774 view.selections.display_ranges(cx),
6775 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6776 );
6777 });
6778 }
6779
6780 #[gpui::test]
6781 fn test_clone(cx: &mut gpui::MutableAppContext) {
6782 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6783 one
6784 two
6785 three[]
6786 four
6787 five[]
6788 "});
6789 cx.set_global(Settings::test(cx));
6790 let buffer = MultiBuffer::build_simple(&text, cx);
6791
6792 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6793
6794 editor.update(cx, |editor, cx| {
6795 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6796 editor.fold_ranges(
6797 [
6798 Point::new(1, 0)..Point::new(2, 0),
6799 Point::new(3, 0)..Point::new(4, 0),
6800 ],
6801 cx,
6802 );
6803 });
6804
6805 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6806 cx.add_window(Default::default(), |cx| editor.clone(cx))
6807 });
6808
6809 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6810 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6811
6812 assert_eq!(
6813 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6814 editor.update(cx, |e, cx| e.display_text(cx))
6815 );
6816 assert_eq!(
6817 cloned_snapshot
6818 .folds_in_range(0..text.len())
6819 .collect::<Vec<_>>(),
6820 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6821 );
6822 assert_set_eq!(
6823 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6824 editor.read(cx).selections.ranges(cx)
6825 );
6826 assert_set_eq!(
6827 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
6828 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
6829 );
6830 }
6831
6832 #[gpui::test]
6833 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6834 cx.set_global(Settings::test(cx));
6835 use workspace::Item;
6836 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
6837 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6838
6839 cx.add_window(Default::default(), |cx| {
6840 let mut editor = build_editor(buffer.clone(), cx);
6841 let handle = cx.handle();
6842 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
6843
6844 fn pop_history(
6845 editor: &mut Editor,
6846 cx: &mut MutableAppContext,
6847 ) -> Option<NavigationEntry> {
6848 editor.nav_history.as_mut().unwrap().pop_backward(cx)
6849 }
6850
6851 // Move the cursor a small distance.
6852 // Nothing is added to the navigation history.
6853 editor.change_selections(None, cx, |s| {
6854 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6855 });
6856 editor.change_selections(None, cx, |s| {
6857 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6858 });
6859 assert!(pop_history(&mut editor, cx).is_none());
6860
6861 // Move the cursor a large distance.
6862 // The history can jump back to the previous position.
6863 editor.change_selections(None, cx, |s| {
6864 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6865 });
6866 let nav_entry = pop_history(&mut editor, cx).unwrap();
6867 editor.navigate(nav_entry.data.unwrap(), cx);
6868 assert_eq!(nav_entry.item.id(), cx.view_id());
6869 assert_eq!(
6870 editor.selections.display_ranges(cx),
6871 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6872 );
6873 assert!(pop_history(&mut editor, cx).is_none());
6874
6875 // Move the cursor a small distance via the mouse.
6876 // Nothing is added to the navigation history.
6877 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6878 editor.end_selection(cx);
6879 assert_eq!(
6880 editor.selections.display_ranges(cx),
6881 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6882 );
6883 assert!(pop_history(&mut editor, cx).is_none());
6884
6885 // Move the cursor a large distance via the mouse.
6886 // The history can jump back to the previous position.
6887 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6888 editor.end_selection(cx);
6889 assert_eq!(
6890 editor.selections.display_ranges(cx),
6891 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6892 );
6893 let nav_entry = pop_history(&mut editor, cx).unwrap();
6894 editor.navigate(nav_entry.data.unwrap(), cx);
6895 assert_eq!(nav_entry.item.id(), cx.view_id());
6896 assert_eq!(
6897 editor.selections.display_ranges(cx),
6898 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6899 );
6900 assert!(pop_history(&mut editor, cx).is_none());
6901
6902 // Set scroll position to check later
6903 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6904 let original_scroll_position = editor.scroll_position;
6905 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6906
6907 // Jump to the end of the document and adjust scroll
6908 editor.move_to_end(&MoveToEnd, cx);
6909 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6910 assert_ne!(editor.scroll_position, original_scroll_position);
6911 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6912
6913 let nav_entry = pop_history(&mut editor, cx).unwrap();
6914 editor.navigate(nav_entry.data.unwrap(), cx);
6915 assert_eq!(editor.scroll_position, original_scroll_position);
6916 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6917
6918 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6919 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6920 invalid_anchor.text_anchor.buffer_id = Some(999);
6921 let invalid_point = Point::new(9999, 0);
6922 editor.navigate(
6923 Box::new(NavigationData {
6924 cursor_anchor: invalid_anchor.clone(),
6925 cursor_position: invalid_point,
6926 scroll_top_anchor: invalid_anchor.clone(),
6927 scroll_top_row: invalid_point.row,
6928 scroll_position: Default::default(),
6929 }),
6930 cx,
6931 );
6932 assert_eq!(
6933 editor.selections.display_ranges(cx),
6934 &[editor.max_point(cx)..editor.max_point(cx)]
6935 );
6936 assert_eq!(
6937 editor.scroll_position(cx),
6938 vec2f(0., editor.max_point(cx).row() as f32)
6939 );
6940
6941 editor
6942 });
6943 }
6944
6945 #[gpui::test]
6946 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6947 cx.set_global(Settings::test(cx));
6948 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6949 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6950
6951 view.update(cx, |view, cx| {
6952 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6953 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6954 view.end_selection(cx);
6955
6956 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6957 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6958 view.end_selection(cx);
6959 assert_eq!(
6960 view.selections.display_ranges(cx),
6961 [
6962 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6963 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6964 ]
6965 );
6966 });
6967
6968 view.update(cx, |view, cx| {
6969 view.cancel(&Cancel, cx);
6970 assert_eq!(
6971 view.selections.display_ranges(cx),
6972 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6973 );
6974 });
6975
6976 view.update(cx, |view, cx| {
6977 view.cancel(&Cancel, cx);
6978 assert_eq!(
6979 view.selections.display_ranges(cx),
6980 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6981 );
6982 });
6983 }
6984
6985 #[gpui::test]
6986 fn test_fold(cx: &mut gpui::MutableAppContext) {
6987 cx.set_global(Settings::test(cx));
6988 let buffer = MultiBuffer::build_simple(
6989 &"
6990 impl Foo {
6991 // Hello!
6992
6993 fn a() {
6994 1
6995 }
6996
6997 fn b() {
6998 2
6999 }
7000
7001 fn c() {
7002 3
7003 }
7004 }
7005 "
7006 .unindent(),
7007 cx,
7008 );
7009 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7010
7011 view.update(cx, |view, cx| {
7012 view.change_selections(None, cx, |s| {
7013 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7014 });
7015 view.fold(&Fold, cx);
7016 assert_eq!(
7017 view.display_text(cx),
7018 "
7019 impl Foo {
7020 // Hello!
7021
7022 fn a() {
7023 1
7024 }
7025
7026 fn b() {…
7027 }
7028
7029 fn c() {…
7030 }
7031 }
7032 "
7033 .unindent(),
7034 );
7035
7036 view.fold(&Fold, cx);
7037 assert_eq!(
7038 view.display_text(cx),
7039 "
7040 impl Foo {…
7041 }
7042 "
7043 .unindent(),
7044 );
7045
7046 view.unfold_lines(&UnfoldLines, cx);
7047 assert_eq!(
7048 view.display_text(cx),
7049 "
7050 impl Foo {
7051 // Hello!
7052
7053 fn a() {
7054 1
7055 }
7056
7057 fn b() {…
7058 }
7059
7060 fn c() {…
7061 }
7062 }
7063 "
7064 .unindent(),
7065 );
7066
7067 view.unfold_lines(&UnfoldLines, cx);
7068 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7069 });
7070 }
7071
7072 #[gpui::test]
7073 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7074 cx.set_global(Settings::test(cx));
7075 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7076 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7077
7078 buffer.update(cx, |buffer, cx| {
7079 buffer.edit(
7080 vec![
7081 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7082 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7083 ],
7084 cx,
7085 );
7086 });
7087
7088 view.update(cx, |view, cx| {
7089 assert_eq!(
7090 view.selections.display_ranges(cx),
7091 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7092 );
7093
7094 view.move_down(&MoveDown, cx);
7095 assert_eq!(
7096 view.selections.display_ranges(cx),
7097 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7098 );
7099
7100 view.move_right(&MoveRight, cx);
7101 assert_eq!(
7102 view.selections.display_ranges(cx),
7103 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7104 );
7105
7106 view.move_left(&MoveLeft, cx);
7107 assert_eq!(
7108 view.selections.display_ranges(cx),
7109 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7110 );
7111
7112 view.move_up(&MoveUp, cx);
7113 assert_eq!(
7114 view.selections.display_ranges(cx),
7115 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7116 );
7117
7118 view.move_to_end(&MoveToEnd, cx);
7119 assert_eq!(
7120 view.selections.display_ranges(cx),
7121 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7122 );
7123
7124 view.move_to_beginning(&MoveToBeginning, cx);
7125 assert_eq!(
7126 view.selections.display_ranges(cx),
7127 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7128 );
7129
7130 view.change_selections(None, cx, |s| {
7131 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7132 });
7133 view.select_to_beginning(&SelectToBeginning, cx);
7134 assert_eq!(
7135 view.selections.display_ranges(cx),
7136 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7137 );
7138
7139 view.select_to_end(&SelectToEnd, cx);
7140 assert_eq!(
7141 view.selections.display_ranges(cx),
7142 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7143 );
7144 });
7145 }
7146
7147 #[gpui::test]
7148 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7149 cx.set_global(Settings::test(cx));
7150 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7151 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7152
7153 assert_eq!('ⓐ'.len_utf8(), 3);
7154 assert_eq!('α'.len_utf8(), 2);
7155
7156 view.update(cx, |view, cx| {
7157 view.fold_ranges(
7158 vec![
7159 Point::new(0, 6)..Point::new(0, 12),
7160 Point::new(1, 2)..Point::new(1, 4),
7161 Point::new(2, 4)..Point::new(2, 8),
7162 ],
7163 cx,
7164 );
7165 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7166
7167 view.move_right(&MoveRight, cx);
7168 assert_eq!(
7169 view.selections.display_ranges(cx),
7170 &[empty_range(0, "ⓐ".len())]
7171 );
7172 view.move_right(&MoveRight, cx);
7173 assert_eq!(
7174 view.selections.display_ranges(cx),
7175 &[empty_range(0, "ⓐⓑ".len())]
7176 );
7177 view.move_right(&MoveRight, cx);
7178 assert_eq!(
7179 view.selections.display_ranges(cx),
7180 &[empty_range(0, "ⓐⓑ…".len())]
7181 );
7182
7183 view.move_down(&MoveDown, cx);
7184 assert_eq!(
7185 view.selections.display_ranges(cx),
7186 &[empty_range(1, "ab…".len())]
7187 );
7188 view.move_left(&MoveLeft, cx);
7189 assert_eq!(
7190 view.selections.display_ranges(cx),
7191 &[empty_range(1, "ab".len())]
7192 );
7193 view.move_left(&MoveLeft, cx);
7194 assert_eq!(
7195 view.selections.display_ranges(cx),
7196 &[empty_range(1, "a".len())]
7197 );
7198
7199 view.move_down(&MoveDown, cx);
7200 assert_eq!(
7201 view.selections.display_ranges(cx),
7202 &[empty_range(2, "α".len())]
7203 );
7204 view.move_right(&MoveRight, cx);
7205 assert_eq!(
7206 view.selections.display_ranges(cx),
7207 &[empty_range(2, "αβ".len())]
7208 );
7209 view.move_right(&MoveRight, cx);
7210 assert_eq!(
7211 view.selections.display_ranges(cx),
7212 &[empty_range(2, "αβ…".len())]
7213 );
7214 view.move_right(&MoveRight, cx);
7215 assert_eq!(
7216 view.selections.display_ranges(cx),
7217 &[empty_range(2, "αβ…ε".len())]
7218 );
7219
7220 view.move_up(&MoveUp, cx);
7221 assert_eq!(
7222 view.selections.display_ranges(cx),
7223 &[empty_range(1, "ab…e".len())]
7224 );
7225 view.move_up(&MoveUp, cx);
7226 assert_eq!(
7227 view.selections.display_ranges(cx),
7228 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7229 );
7230 view.move_left(&MoveLeft, cx);
7231 assert_eq!(
7232 view.selections.display_ranges(cx),
7233 &[empty_range(0, "ⓐⓑ…".len())]
7234 );
7235 view.move_left(&MoveLeft, cx);
7236 assert_eq!(
7237 view.selections.display_ranges(cx),
7238 &[empty_range(0, "ⓐⓑ".len())]
7239 );
7240 view.move_left(&MoveLeft, cx);
7241 assert_eq!(
7242 view.selections.display_ranges(cx),
7243 &[empty_range(0, "ⓐ".len())]
7244 );
7245 });
7246 }
7247
7248 #[gpui::test]
7249 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7250 cx.set_global(Settings::test(cx));
7251 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7252 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7253 view.update(cx, |view, cx| {
7254 view.change_selections(None, cx, |s| {
7255 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7256 });
7257 view.move_down(&MoveDown, cx);
7258 assert_eq!(
7259 view.selections.display_ranges(cx),
7260 &[empty_range(1, "abcd".len())]
7261 );
7262
7263 view.move_down(&MoveDown, cx);
7264 assert_eq!(
7265 view.selections.display_ranges(cx),
7266 &[empty_range(2, "αβγ".len())]
7267 );
7268
7269 view.move_down(&MoveDown, cx);
7270 assert_eq!(
7271 view.selections.display_ranges(cx),
7272 &[empty_range(3, "abcd".len())]
7273 );
7274
7275 view.move_down(&MoveDown, cx);
7276 assert_eq!(
7277 view.selections.display_ranges(cx),
7278 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7279 );
7280
7281 view.move_up(&MoveUp, cx);
7282 assert_eq!(
7283 view.selections.display_ranges(cx),
7284 &[empty_range(3, "abcd".len())]
7285 );
7286
7287 view.move_up(&MoveUp, cx);
7288 assert_eq!(
7289 view.selections.display_ranges(cx),
7290 &[empty_range(2, "αβγ".len())]
7291 );
7292 });
7293 }
7294
7295 #[gpui::test]
7296 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7297 cx.set_global(Settings::test(cx));
7298 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7299 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7300 view.update(cx, |view, cx| {
7301 view.change_selections(None, cx, |s| {
7302 s.select_display_ranges([
7303 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7304 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7305 ]);
7306 });
7307 });
7308
7309 view.update(cx, |view, cx| {
7310 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7311 assert_eq!(
7312 view.selections.display_ranges(cx),
7313 &[
7314 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7315 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7316 ]
7317 );
7318 });
7319
7320 view.update(cx, |view, cx| {
7321 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7322 assert_eq!(
7323 view.selections.display_ranges(cx),
7324 &[
7325 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7326 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7327 ]
7328 );
7329 });
7330
7331 view.update(cx, |view, cx| {
7332 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7333 assert_eq!(
7334 view.selections.display_ranges(cx),
7335 &[
7336 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7337 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7338 ]
7339 );
7340 });
7341
7342 view.update(cx, |view, cx| {
7343 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7344 assert_eq!(
7345 view.selections.display_ranges(cx),
7346 &[
7347 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7348 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7349 ]
7350 );
7351 });
7352
7353 // Moving to the end of line again is a no-op.
7354 view.update(cx, |view, cx| {
7355 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7356 assert_eq!(
7357 view.selections.display_ranges(cx),
7358 &[
7359 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7360 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7361 ]
7362 );
7363 });
7364
7365 view.update(cx, |view, cx| {
7366 view.move_left(&MoveLeft, cx);
7367 view.select_to_beginning_of_line(
7368 &SelectToBeginningOfLine {
7369 stop_at_soft_wraps: true,
7370 },
7371 cx,
7372 );
7373 assert_eq!(
7374 view.selections.display_ranges(cx),
7375 &[
7376 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7377 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7378 ]
7379 );
7380 });
7381
7382 view.update(cx, |view, cx| {
7383 view.select_to_beginning_of_line(
7384 &SelectToBeginningOfLine {
7385 stop_at_soft_wraps: true,
7386 },
7387 cx,
7388 );
7389 assert_eq!(
7390 view.selections.display_ranges(cx),
7391 &[
7392 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7393 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7394 ]
7395 );
7396 });
7397
7398 view.update(cx, |view, cx| {
7399 view.select_to_beginning_of_line(
7400 &SelectToBeginningOfLine {
7401 stop_at_soft_wraps: true,
7402 },
7403 cx,
7404 );
7405 assert_eq!(
7406 view.selections.display_ranges(cx),
7407 &[
7408 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7409 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7410 ]
7411 );
7412 });
7413
7414 view.update(cx, |view, cx| {
7415 view.select_to_end_of_line(
7416 &SelectToEndOfLine {
7417 stop_at_soft_wraps: true,
7418 },
7419 cx,
7420 );
7421 assert_eq!(
7422 view.selections.display_ranges(cx),
7423 &[
7424 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7425 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7426 ]
7427 );
7428 });
7429
7430 view.update(cx, |view, cx| {
7431 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7432 assert_eq!(view.display_text(cx), "ab\n de");
7433 assert_eq!(
7434 view.selections.display_ranges(cx),
7435 &[
7436 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7437 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7438 ]
7439 );
7440 });
7441
7442 view.update(cx, |view, cx| {
7443 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7444 assert_eq!(view.display_text(cx), "\n");
7445 assert_eq!(
7446 view.selections.display_ranges(cx),
7447 &[
7448 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7449 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7450 ]
7451 );
7452 });
7453 }
7454
7455 #[gpui::test]
7456 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7457 cx.set_global(Settings::test(cx));
7458 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7459 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7460 view.update(cx, |view, cx| {
7461 view.change_selections(None, cx, |s| {
7462 s.select_display_ranges([
7463 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7464 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7465 ])
7466 });
7467
7468 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7469 assert_selection_ranges(
7470 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7471 vec![('<', '>'), ('[', ']')],
7472 view,
7473 cx,
7474 );
7475
7476 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7477 assert_selection_ranges(
7478 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7479 vec![('<', '>'), ('[', ']')],
7480 view,
7481 cx,
7482 );
7483
7484 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7485 assert_selection_ranges(
7486 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7487 vec![('<', '>'), ('[', ']')],
7488 view,
7489 cx,
7490 );
7491
7492 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7493 assert_selection_ranges(
7494 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7495 vec![('<', '>'), ('[', ']')],
7496 view,
7497 cx,
7498 );
7499
7500 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7501 assert_selection_ranges(
7502 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7503 vec![('<', '>'), ('[', ']')],
7504 view,
7505 cx,
7506 );
7507
7508 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7509 assert_selection_ranges(
7510 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7511 vec![('<', '>'), ('[', ']')],
7512 view,
7513 cx,
7514 );
7515
7516 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7517 assert_selection_ranges(
7518 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7519 vec![('<', '>'), ('[', ']')],
7520 view,
7521 cx,
7522 );
7523
7524 view.move_to_next_word_end(&MoveToNextWordEnd, 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_right(&MoveRight, cx);
7533 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7534 assert_selection_ranges(
7535 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7536 vec![('<', '>'), ('[', ']')],
7537 view,
7538 cx,
7539 );
7540
7541 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7542 assert_selection_ranges(
7543 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7544 vec![('<', '>'), ('[', ']')],
7545 view,
7546 cx,
7547 );
7548
7549 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7550 assert_selection_ranges(
7551 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7552 vec![('<', '>'), ('[', ']')],
7553 view,
7554 cx,
7555 );
7556 });
7557 }
7558
7559 #[gpui::test]
7560 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7561 cx.set_global(Settings::test(cx));
7562 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7563 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7564
7565 view.update(cx, |view, cx| {
7566 view.set_wrap_width(Some(140.), cx);
7567 assert_eq!(
7568 view.display_text(cx),
7569 "use one::{\n two::three::\n four::five\n};"
7570 );
7571
7572 view.change_selections(None, cx, |s| {
7573 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7574 });
7575
7576 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7577 assert_eq!(
7578 view.selections.display_ranges(cx),
7579 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7580 );
7581
7582 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7583 assert_eq!(
7584 view.selections.display_ranges(cx),
7585 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7586 );
7587
7588 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7589 assert_eq!(
7590 view.selections.display_ranges(cx),
7591 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7592 );
7593
7594 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7595 assert_eq!(
7596 view.selections.display_ranges(cx),
7597 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7598 );
7599
7600 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7601 assert_eq!(
7602 view.selections.display_ranges(cx),
7603 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7604 );
7605
7606 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7607 assert_eq!(
7608 view.selections.display_ranges(cx),
7609 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7610 );
7611 });
7612 }
7613
7614 #[gpui::test]
7615 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7616 cx.set_global(Settings::test(cx));
7617 let (text, ranges) = marked_text_ranges("one [two three] four");
7618 let buffer = MultiBuffer::build_simple(&text, cx);
7619
7620 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7621
7622 editor.update(cx, |editor, cx| {
7623 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7624 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7625 assert_eq!(editor.text(cx), " four");
7626 });
7627 }
7628
7629 #[gpui::test]
7630 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7631 cx.set_global(Settings::test(cx));
7632 let buffer = MultiBuffer::build_simple("one two three four", cx);
7633 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7634
7635 view.update(cx, |view, cx| {
7636 view.change_selections(None, cx, |s| {
7637 s.select_display_ranges([
7638 // an empty selection - the preceding word fragment is deleted
7639 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7640 // characters selected - they are deleted
7641 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7642 ])
7643 });
7644 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7645 });
7646
7647 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7648
7649 view.update(cx, |view, cx| {
7650 view.change_selections(None, cx, |s| {
7651 s.select_display_ranges([
7652 // an empty selection - the following word fragment is deleted
7653 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7654 // characters selected - they are deleted
7655 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7656 ])
7657 });
7658 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7659 });
7660
7661 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7662 }
7663
7664 #[gpui::test]
7665 fn test_newline(cx: &mut gpui::MutableAppContext) {
7666 cx.set_global(Settings::test(cx));
7667 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7668 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7669
7670 view.update(cx, |view, cx| {
7671 view.change_selections(None, cx, |s| {
7672 s.select_display_ranges([
7673 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7674 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7675 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7676 ])
7677 });
7678
7679 view.newline(&Newline, cx);
7680 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7681 });
7682 }
7683
7684 #[gpui::test]
7685 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7686 cx.set_global(Settings::test(cx));
7687 let buffer = MultiBuffer::build_simple(
7688 "
7689 a
7690 b(
7691 X
7692 )
7693 c(
7694 X
7695 )
7696 "
7697 .unindent()
7698 .as_str(),
7699 cx,
7700 );
7701
7702 let (_, editor) = cx.add_window(Default::default(), |cx| {
7703 let mut editor = build_editor(buffer.clone(), cx);
7704 editor.change_selections(None, cx, |s| {
7705 s.select_ranges([
7706 Point::new(2, 4)..Point::new(2, 5),
7707 Point::new(5, 4)..Point::new(5, 5),
7708 ])
7709 });
7710 editor
7711 });
7712
7713 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7714 buffer.update(cx, |buffer, cx| {
7715 buffer.edit(
7716 [
7717 (Point::new(1, 2)..Point::new(3, 0), ""),
7718 (Point::new(4, 2)..Point::new(6, 0), ""),
7719 ],
7720 cx,
7721 );
7722 assert_eq!(
7723 buffer.read(cx).text(),
7724 "
7725 a
7726 b()
7727 c()
7728 "
7729 .unindent()
7730 );
7731 });
7732
7733 editor.update(cx, |editor, cx| {
7734 assert_eq!(
7735 editor.selections.ranges(cx),
7736 &[
7737 Point::new(1, 2)..Point::new(1, 2),
7738 Point::new(2, 2)..Point::new(2, 2),
7739 ],
7740 );
7741
7742 editor.newline(&Newline, cx);
7743 assert_eq!(
7744 editor.text(cx),
7745 "
7746 a
7747 b(
7748 )
7749 c(
7750 )
7751 "
7752 .unindent()
7753 );
7754
7755 // The selections are moved after the inserted newlines
7756 assert_eq!(
7757 editor.selections.ranges(cx),
7758 &[
7759 Point::new(2, 0)..Point::new(2, 0),
7760 Point::new(4, 0)..Point::new(4, 0),
7761 ],
7762 );
7763 });
7764 }
7765
7766 #[gpui::test]
7767 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7768 cx.set_global(Settings::test(cx));
7769 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7770 let (_, editor) = cx.add_window(Default::default(), |cx| {
7771 let mut editor = build_editor(buffer.clone(), cx);
7772 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7773 editor
7774 });
7775
7776 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7777 buffer.update(cx, |buffer, cx| {
7778 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7779 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7780 });
7781
7782 editor.update(cx, |editor, cx| {
7783 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7784
7785 editor.insert("Z", cx);
7786 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7787
7788 // The selections are moved after the inserted characters
7789 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7790 });
7791 }
7792
7793 #[gpui::test]
7794 async fn test_tab(cx: &mut gpui::TestAppContext) {
7795 let mut cx = EditorTestContext::new(cx).await;
7796 cx.update(|cx| {
7797 cx.update_global::<Settings, _, _>(|settings, _| {
7798 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
7799 });
7800 });
7801 cx.set_state(indoc! {"
7802 |ab|c
7803 |🏀|🏀|efg
7804 d|
7805 "});
7806 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7807 cx.assert_editor_state(indoc! {"
7808 |ab |c
7809 |🏀 |🏀 |efg
7810 d |
7811 "});
7812 }
7813
7814 #[gpui::test]
7815 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7816 let mut cx = EditorTestContext::new(cx).await;
7817
7818 cx.set_state(indoc! {"
7819 [one} [two}
7820 three
7821 four"});
7822 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7823 cx.assert_editor_state(indoc! {"
7824 [one} [two}
7825 three
7826 four"});
7827
7828 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7829 cx.assert_editor_state(indoc! {"
7830 [one} [two}
7831 three
7832 four"});
7833
7834 // select across line ending
7835 cx.set_state(indoc! {"
7836 one two
7837 t[hree
7838 } four"});
7839 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7840 cx.assert_editor_state(indoc! {"
7841 one two
7842 t[hree
7843 } four"});
7844
7845 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7846 cx.assert_editor_state(indoc! {"
7847 one two
7848 t[hree
7849 } four"});
7850
7851 // Ensure that indenting/outdenting works when the cursor is at column 0.
7852 cx.set_state(indoc! {"
7853 one two
7854 |three
7855 four"});
7856 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7857 cx.assert_editor_state(indoc! {"
7858 one two
7859 |three
7860 four"});
7861
7862 cx.set_state(indoc! {"
7863 one two
7864 | three
7865 four"});
7866 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7867 cx.assert_editor_state(indoc! {"
7868 one two
7869 |three
7870 four"});
7871 }
7872
7873 #[gpui::test]
7874 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7875 let mut cx = EditorTestContext::new(cx).await;
7876 cx.update(|cx| {
7877 cx.update_global::<Settings, _, _>(|settings, _| {
7878 settings.editor_overrides.hard_tabs = Some(true);
7879 });
7880 });
7881
7882 // select two ranges on one line
7883 cx.set_state(indoc! {"
7884 [one} [two}
7885 three
7886 four"});
7887 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7888 cx.assert_editor_state(indoc! {"
7889 \t[one} [two}
7890 three
7891 four"});
7892 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7893 cx.assert_editor_state(indoc! {"
7894 \t\t[one} [two}
7895 three
7896 four"});
7897 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7898 cx.assert_editor_state(indoc! {"
7899 \t[one} [two}
7900 three
7901 four"});
7902 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7903 cx.assert_editor_state(indoc! {"
7904 [one} [two}
7905 three
7906 four"});
7907
7908 // select across a line ending
7909 cx.set_state(indoc! {"
7910 one two
7911 t[hree
7912 }four"});
7913 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7914 cx.assert_editor_state(indoc! {"
7915 one two
7916 \tt[hree
7917 }four"});
7918 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7919 cx.assert_editor_state(indoc! {"
7920 one two
7921 \t\tt[hree
7922 }four"});
7923 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7924 cx.assert_editor_state(indoc! {"
7925 one two
7926 \tt[hree
7927 }four"});
7928 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7929 cx.assert_editor_state(indoc! {"
7930 one two
7931 t[hree
7932 }four"});
7933
7934 // Ensure that indenting/outdenting works when the cursor is at column 0.
7935 cx.set_state(indoc! {"
7936 one two
7937 |three
7938 four"});
7939 cx.assert_editor_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 one two
7946 \t|three
7947 four"});
7948 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7949 cx.assert_editor_state(indoc! {"
7950 one two
7951 |three
7952 four"});
7953 }
7954
7955 #[gpui::test]
7956 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7957 cx.set_global(
7958 Settings::test(cx)
7959 .with_language_defaults(
7960 "TOML",
7961 EditorSettings {
7962 tab_size: Some(2.try_into().unwrap()),
7963 ..Default::default()
7964 },
7965 )
7966 .with_language_defaults(
7967 "Rust",
7968 EditorSettings {
7969 tab_size: Some(4.try_into().unwrap()),
7970 ..Default::default()
7971 },
7972 ),
7973 );
7974 let toml_language = Arc::new(Language::new(
7975 LanguageConfig {
7976 name: "TOML".into(),
7977 ..Default::default()
7978 },
7979 None,
7980 ));
7981 let rust_language = Arc::new(Language::new(
7982 LanguageConfig {
7983 name: "Rust".into(),
7984 ..Default::default()
7985 },
7986 None,
7987 ));
7988
7989 let toml_buffer = cx
7990 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7991 let rust_buffer = cx.add_model(|cx| {
7992 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7993 });
7994 let multibuffer = cx.add_model(|cx| {
7995 let mut multibuffer = MultiBuffer::new(0);
7996 multibuffer.push_excerpts(
7997 toml_buffer.clone(),
7998 [ExcerptRange {
7999 context: Point::new(0, 0)..Point::new(2, 0),
8000 primary: None,
8001 }],
8002 cx,
8003 );
8004 multibuffer.push_excerpts(
8005 rust_buffer.clone(),
8006 [ExcerptRange {
8007 context: Point::new(0, 0)..Point::new(1, 0),
8008 primary: None,
8009 }],
8010 cx,
8011 );
8012 multibuffer
8013 });
8014
8015 cx.add_window(Default::default(), |cx| {
8016 let mut editor = build_editor(multibuffer, cx);
8017
8018 assert_eq!(
8019 editor.text(cx),
8020 indoc! {"
8021 a = 1
8022 b = 2
8023
8024 const c: usize = 3;
8025 "}
8026 );
8027
8028 select_ranges(
8029 &mut editor,
8030 indoc! {"
8031 [a] = 1
8032 b = 2
8033
8034 [const c:] usize = 3;
8035 "},
8036 cx,
8037 );
8038
8039 editor.tab(&Tab, cx);
8040 assert_text_with_selections(
8041 &mut editor,
8042 indoc! {"
8043 [a] = 1
8044 b = 2
8045
8046 [const c:] usize = 3;
8047 "},
8048 cx,
8049 );
8050 editor.tab_prev(&TabPrev, cx);
8051 assert_text_with_selections(
8052 &mut editor,
8053 indoc! {"
8054 [a] = 1
8055 b = 2
8056
8057 [const c:] usize = 3;
8058 "},
8059 cx,
8060 );
8061
8062 editor
8063 });
8064 }
8065
8066 #[gpui::test]
8067 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8068 let mut cx = EditorTestContext::new(cx).await;
8069 // Basic backspace
8070 cx.set_state(indoc! {"
8071 on|e two three
8072 fou[r} five six
8073 seven {eight nine
8074 ]ten"});
8075 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8076 cx.assert_editor_state(indoc! {"
8077 o|e two three
8078 fou| five six
8079 seven |ten"});
8080
8081 // Test backspace inside and around indents
8082 cx.set_state(indoc! {"
8083 zero
8084 |one
8085 |two
8086 | | | three
8087 | | four"});
8088 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8089 cx.assert_editor_state(indoc! {"
8090 zero
8091 |one
8092 |two
8093 | three| four"});
8094
8095 // Test backspace with line_mode set to true
8096 cx.update_editor(|e, _| e.selections.line_mode = true);
8097 cx.set_state(indoc! {"
8098 The |quick |brown
8099 fox jumps over
8100 the lazy dog
8101 |The qu[ick b}rown"});
8102 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8103 cx.assert_editor_state(indoc! {"
8104 |fox jumps over
8105 the lazy dog|"});
8106 }
8107
8108 #[gpui::test]
8109 async fn test_delete(cx: &mut gpui::TestAppContext) {
8110 let mut cx = EditorTestContext::new(cx).await;
8111
8112 cx.set_state(indoc! {"
8113 on|e two three
8114 fou[r} five six
8115 seven {eight nine
8116 ]ten"});
8117 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8118 cx.assert_editor_state(indoc! {"
8119 on| two three
8120 fou| five six
8121 seven |ten"});
8122
8123 // Test backspace with line_mode set to true
8124 cx.update_editor(|e, _| e.selections.line_mode = true);
8125 cx.set_state(indoc! {"
8126 The |quick |brown
8127 fox {jum]ps over
8128 the lazy dog
8129 |The qu[ick b}rown"});
8130 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8131 cx.assert_editor_state("|the lazy dog|");
8132 }
8133
8134 #[gpui::test]
8135 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8136 cx.set_global(Settings::test(cx));
8137 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8138 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8139 view.update(cx, |view, cx| {
8140 view.change_selections(None, cx, |s| {
8141 s.select_display_ranges([
8142 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8143 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8144 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8145 ])
8146 });
8147 view.delete_line(&DeleteLine, cx);
8148 assert_eq!(view.display_text(cx), "ghi");
8149 assert_eq!(
8150 view.selections.display_ranges(cx),
8151 vec![
8152 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8153 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8154 ]
8155 );
8156 });
8157
8158 cx.set_global(Settings::test(cx));
8159 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8160 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8161 view.update(cx, |view, cx| {
8162 view.change_selections(None, cx, |s| {
8163 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8164 });
8165 view.delete_line(&DeleteLine, cx);
8166 assert_eq!(view.display_text(cx), "ghi\n");
8167 assert_eq!(
8168 view.selections.display_ranges(cx),
8169 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8170 );
8171 });
8172 }
8173
8174 #[gpui::test]
8175 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8176 cx.set_global(Settings::test(cx));
8177 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8178 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8179 view.update(cx, |view, cx| {
8180 view.change_selections(None, cx, |s| {
8181 s.select_display_ranges([
8182 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8183 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8184 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8185 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8186 ])
8187 });
8188 view.duplicate_line(&DuplicateLine, cx);
8189 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8190 assert_eq!(
8191 view.selections.display_ranges(cx),
8192 vec![
8193 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8194 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8195 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8196 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8197 ]
8198 );
8199 });
8200
8201 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8202 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8203 view.update(cx, |view, cx| {
8204 view.change_selections(None, cx, |s| {
8205 s.select_display_ranges([
8206 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8207 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8208 ])
8209 });
8210 view.duplicate_line(&DuplicateLine, cx);
8211 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8212 assert_eq!(
8213 view.selections.display_ranges(cx),
8214 vec![
8215 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8216 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8217 ]
8218 );
8219 });
8220 }
8221
8222 #[gpui::test]
8223 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8224 cx.set_global(Settings::test(cx));
8225 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8226 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8227 view.update(cx, |view, cx| {
8228 view.fold_ranges(
8229 vec![
8230 Point::new(0, 2)..Point::new(1, 2),
8231 Point::new(2, 3)..Point::new(4, 1),
8232 Point::new(7, 0)..Point::new(8, 4),
8233 ],
8234 cx,
8235 );
8236 view.change_selections(None, cx, |s| {
8237 s.select_display_ranges([
8238 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8239 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8240 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8241 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8242 ])
8243 });
8244 assert_eq!(
8245 view.display_text(cx),
8246 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8247 );
8248
8249 view.move_line_up(&MoveLineUp, cx);
8250 assert_eq!(
8251 view.display_text(cx),
8252 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8253 );
8254 assert_eq!(
8255 view.selections.display_ranges(cx),
8256 vec![
8257 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8258 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8259 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8260 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8261 ]
8262 );
8263 });
8264
8265 view.update(cx, |view, cx| {
8266 view.move_line_down(&MoveLineDown, cx);
8267 assert_eq!(
8268 view.display_text(cx),
8269 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8270 );
8271 assert_eq!(
8272 view.selections.display_ranges(cx),
8273 vec![
8274 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8275 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8276 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8277 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8278 ]
8279 );
8280 });
8281
8282 view.update(cx, |view, cx| {
8283 view.move_line_down(&MoveLineDown, cx);
8284 assert_eq!(
8285 view.display_text(cx),
8286 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8287 );
8288 assert_eq!(
8289 view.selections.display_ranges(cx),
8290 vec![
8291 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8292 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8293 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8294 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8295 ]
8296 );
8297 });
8298
8299 view.update(cx, |view, cx| {
8300 view.move_line_up(&MoveLineUp, cx);
8301 assert_eq!(
8302 view.display_text(cx),
8303 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8304 );
8305 assert_eq!(
8306 view.selections.display_ranges(cx),
8307 vec![
8308 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8309 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8310 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8311 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8312 ]
8313 );
8314 });
8315 }
8316
8317 #[gpui::test]
8318 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8319 cx.set_global(Settings::test(cx));
8320 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8321 let snapshot = buffer.read(cx).snapshot(cx);
8322 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8323 editor.update(cx, |editor, cx| {
8324 editor.insert_blocks(
8325 [BlockProperties {
8326 style: BlockStyle::Fixed,
8327 position: snapshot.anchor_after(Point::new(2, 0)),
8328 disposition: BlockDisposition::Below,
8329 height: 1,
8330 render: Arc::new(|_| Empty::new().boxed()),
8331 }],
8332 cx,
8333 );
8334 editor.change_selections(None, cx, |s| {
8335 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8336 });
8337 editor.move_line_down(&MoveLineDown, cx);
8338 });
8339 }
8340
8341 #[gpui::test]
8342 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8343 cx.set_global(Settings::test(cx));
8344
8345 cx.add_window(Default::default(), |cx| {
8346 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8347
8348 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8349 editor.transpose(&Default::default(), cx);
8350 assert_eq!(editor.text(cx), "bac");
8351 assert_eq!(editor.selections.ranges(cx), [2..2]);
8352
8353 editor.transpose(&Default::default(), cx);
8354 assert_eq!(editor.text(cx), "bca");
8355 assert_eq!(editor.selections.ranges(cx), [3..3]);
8356
8357 editor.transpose(&Default::default(), cx);
8358 assert_eq!(editor.text(cx), "bac");
8359 assert_eq!(editor.selections.ranges(cx), [3..3]);
8360
8361 editor
8362 })
8363 .1;
8364
8365 cx.add_window(Default::default(), |cx| {
8366 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8367
8368 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8369 editor.transpose(&Default::default(), cx);
8370 assert_eq!(editor.text(cx), "acb\nde");
8371 assert_eq!(editor.selections.ranges(cx), [3..3]);
8372
8373 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8374 editor.transpose(&Default::default(), cx);
8375 assert_eq!(editor.text(cx), "acbd\ne");
8376 assert_eq!(editor.selections.ranges(cx), [5..5]);
8377
8378 editor.transpose(&Default::default(), cx);
8379 assert_eq!(editor.text(cx), "acbde\n");
8380 assert_eq!(editor.selections.ranges(cx), [6..6]);
8381
8382 editor.transpose(&Default::default(), cx);
8383 assert_eq!(editor.text(cx), "acbd\ne");
8384 assert_eq!(editor.selections.ranges(cx), [6..6]);
8385
8386 editor
8387 })
8388 .1;
8389
8390 cx.add_window(Default::default(), |cx| {
8391 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8392
8393 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8394 editor.transpose(&Default::default(), cx);
8395 assert_eq!(editor.text(cx), "bacd\ne");
8396 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8397
8398 editor.transpose(&Default::default(), cx);
8399 assert_eq!(editor.text(cx), "bcade\n");
8400 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8401
8402 editor.transpose(&Default::default(), cx);
8403 assert_eq!(editor.text(cx), "bcda\ne");
8404 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8405
8406 editor.transpose(&Default::default(), cx);
8407 assert_eq!(editor.text(cx), "bcade\n");
8408 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8409
8410 editor.transpose(&Default::default(), cx);
8411 assert_eq!(editor.text(cx), "bcaed\n");
8412 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8413
8414 editor
8415 })
8416 .1;
8417
8418 cx.add_window(Default::default(), |cx| {
8419 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8420
8421 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8422 editor.transpose(&Default::default(), cx);
8423 assert_eq!(editor.text(cx), "🏀🍐✋");
8424 assert_eq!(editor.selections.ranges(cx), [8..8]);
8425
8426 editor.transpose(&Default::default(), cx);
8427 assert_eq!(editor.text(cx), "🏀✋🍐");
8428 assert_eq!(editor.selections.ranges(cx), [11..11]);
8429
8430 editor.transpose(&Default::default(), cx);
8431 assert_eq!(editor.text(cx), "🏀🍐✋");
8432 assert_eq!(editor.selections.ranges(cx), [11..11]);
8433
8434 editor
8435 })
8436 .1;
8437 }
8438
8439 #[gpui::test]
8440 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8441 let mut cx = EditorTestContext::new(cx).await;
8442
8443 cx.set_state("[one✅ }two [three }four [five }six ");
8444 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8445 cx.assert_editor_state("|two |four |six ");
8446
8447 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8448 cx.set_state("two |four |six |");
8449 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8450 cx.assert_editor_state("two one✅ |four three |six five |");
8451
8452 // Paste again but with only two cursors. Since the number of cursors doesn't
8453 // match the number of slices in the clipboard, the entire clipboard text
8454 // is pasted at each cursor.
8455 cx.set_state("|two one✅ four three six five |");
8456 cx.update_editor(|e, cx| {
8457 e.handle_input("( ", cx);
8458 e.paste(&Paste, cx);
8459 e.handle_input(") ", cx);
8460 });
8461 cx.assert_editor_state(indoc! {"
8462 ( one✅
8463 three
8464 five ) |two one✅ four three six five ( one✅
8465 three
8466 five ) |"});
8467
8468 // Cut with three selections, one of which is full-line.
8469 cx.set_state(indoc! {"
8470 1[2}3
8471 4|567
8472 [8}9"});
8473 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8474 cx.assert_editor_state(indoc! {"
8475 1|3
8476 |9"});
8477
8478 // Paste with three selections, noticing how the copied selection that was full-line
8479 // gets inserted before the second cursor.
8480 cx.set_state(indoc! {"
8481 1|3
8482 9|
8483 [o}ne"});
8484 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8485 cx.assert_editor_state(indoc! {"
8486 12|3
8487 4567
8488 9|
8489 8|ne"});
8490
8491 // Copy with a single cursor only, which writes the whole line into the clipboard.
8492 cx.set_state(indoc! {"
8493 The quick brown
8494 fox ju|mps over
8495 the lazy dog"});
8496 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8497 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8498
8499 // Paste with three selections, noticing how the copied full-line selection is inserted
8500 // before the empty selections but replaces the selection that is non-empty.
8501 cx.set_state(indoc! {"
8502 T|he quick brown
8503 [fo}x jumps over
8504 t|he lazy dog"});
8505 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8506 cx.assert_editor_state(indoc! {"
8507 fox jumps over
8508 T|he quick brown
8509 fox jumps over
8510 |x jumps over
8511 fox jumps over
8512 t|he lazy dog"});
8513 }
8514
8515 #[gpui::test]
8516 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8517 cx.set_global(Settings::test(cx));
8518 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8519 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8520 view.update(cx, |view, cx| {
8521 view.select_all(&SelectAll, cx);
8522 assert_eq!(
8523 view.selections.display_ranges(cx),
8524 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8525 );
8526 });
8527 }
8528
8529 #[gpui::test]
8530 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8531 cx.set_global(Settings::test(cx));
8532 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8533 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8534 view.update(cx, |view, cx| {
8535 view.change_selections(None, cx, |s| {
8536 s.select_display_ranges([
8537 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8538 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8539 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8540 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8541 ])
8542 });
8543 view.select_line(&SelectLine, cx);
8544 assert_eq!(
8545 view.selections.display_ranges(cx),
8546 vec![
8547 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8548 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8549 ]
8550 );
8551 });
8552
8553 view.update(cx, |view, cx| {
8554 view.select_line(&SelectLine, cx);
8555 assert_eq!(
8556 view.selections.display_ranges(cx),
8557 vec![
8558 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8559 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8560 ]
8561 );
8562 });
8563
8564 view.update(cx, |view, cx| {
8565 view.select_line(&SelectLine, cx);
8566 assert_eq!(
8567 view.selections.display_ranges(cx),
8568 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8569 );
8570 });
8571 }
8572
8573 #[gpui::test]
8574 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8575 cx.set_global(Settings::test(cx));
8576 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8577 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8578 view.update(cx, |view, cx| {
8579 view.fold_ranges(
8580 vec![
8581 Point::new(0, 2)..Point::new(1, 2),
8582 Point::new(2, 3)..Point::new(4, 1),
8583 Point::new(7, 0)..Point::new(8, 4),
8584 ],
8585 cx,
8586 );
8587 view.change_selections(None, cx, |s| {
8588 s.select_display_ranges([
8589 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8590 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8591 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8592 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8593 ])
8594 });
8595 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8596 });
8597
8598 view.update(cx, |view, cx| {
8599 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8600 assert_eq!(
8601 view.display_text(cx),
8602 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8603 );
8604 assert_eq!(
8605 view.selections.display_ranges(cx),
8606 [
8607 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8608 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8609 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8610 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8611 ]
8612 );
8613 });
8614
8615 view.update(cx, |view, cx| {
8616 view.change_selections(None, cx, |s| {
8617 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8618 });
8619 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8620 assert_eq!(
8621 view.display_text(cx),
8622 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8623 );
8624 assert_eq!(
8625 view.selections.display_ranges(cx),
8626 [
8627 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8628 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8629 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8630 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8631 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8632 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8633 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8634 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8635 ]
8636 );
8637 });
8638 }
8639
8640 #[gpui::test]
8641 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8642 cx.set_global(Settings::test(cx));
8643 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8644 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8645
8646 view.update(cx, |view, cx| {
8647 view.change_selections(None, cx, |s| {
8648 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8649 });
8650 });
8651 view.update(cx, |view, cx| {
8652 view.add_selection_above(&AddSelectionAbove, cx);
8653 assert_eq!(
8654 view.selections.display_ranges(cx),
8655 vec![
8656 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8657 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8658 ]
8659 );
8660 });
8661
8662 view.update(cx, |view, cx| {
8663 view.add_selection_above(&AddSelectionAbove, cx);
8664 assert_eq!(
8665 view.selections.display_ranges(cx),
8666 vec![
8667 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8668 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8669 ]
8670 );
8671 });
8672
8673 view.update(cx, |view, cx| {
8674 view.add_selection_below(&AddSelectionBelow, cx);
8675 assert_eq!(
8676 view.selections.display_ranges(cx),
8677 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8678 );
8679
8680 view.undo_selection(&UndoSelection, cx);
8681 assert_eq!(
8682 view.selections.display_ranges(cx),
8683 vec![
8684 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8685 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8686 ]
8687 );
8688
8689 view.redo_selection(&RedoSelection, cx);
8690 assert_eq!(
8691 view.selections.display_ranges(cx),
8692 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8693 );
8694 });
8695
8696 view.update(cx, |view, cx| {
8697 view.add_selection_below(&AddSelectionBelow, cx);
8698 assert_eq!(
8699 view.selections.display_ranges(cx),
8700 vec![
8701 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8702 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8703 ]
8704 );
8705 });
8706
8707 view.update(cx, |view, cx| {
8708 view.add_selection_below(&AddSelectionBelow, cx);
8709 assert_eq!(
8710 view.selections.display_ranges(cx),
8711 vec![
8712 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8713 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8714 ]
8715 );
8716 });
8717
8718 view.update(cx, |view, cx| {
8719 view.change_selections(None, cx, |s| {
8720 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8721 });
8722 });
8723 view.update(cx, |view, cx| {
8724 view.add_selection_below(&AddSelectionBelow, cx);
8725 assert_eq!(
8726 view.selections.display_ranges(cx),
8727 vec![
8728 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8729 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8730 ]
8731 );
8732 });
8733
8734 view.update(cx, |view, cx| {
8735 view.add_selection_below(&AddSelectionBelow, cx);
8736 assert_eq!(
8737 view.selections.display_ranges(cx),
8738 vec![
8739 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8740 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8741 ]
8742 );
8743 });
8744
8745 view.update(cx, |view, cx| {
8746 view.add_selection_above(&AddSelectionAbove, cx);
8747 assert_eq!(
8748 view.selections.display_ranges(cx),
8749 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8750 );
8751 });
8752
8753 view.update(cx, |view, cx| {
8754 view.add_selection_above(&AddSelectionAbove, cx);
8755 assert_eq!(
8756 view.selections.display_ranges(cx),
8757 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8758 );
8759 });
8760
8761 view.update(cx, |view, cx| {
8762 view.change_selections(None, cx, |s| {
8763 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8764 });
8765 view.add_selection_below(&AddSelectionBelow, cx);
8766 assert_eq!(
8767 view.selections.display_ranges(cx),
8768 vec![
8769 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8770 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8771 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8772 ]
8773 );
8774 });
8775
8776 view.update(cx, |view, cx| {
8777 view.add_selection_below(&AddSelectionBelow, cx);
8778 assert_eq!(
8779 view.selections.display_ranges(cx),
8780 vec![
8781 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8782 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8783 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8784 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8785 ]
8786 );
8787 });
8788
8789 view.update(cx, |view, cx| {
8790 view.add_selection_above(&AddSelectionAbove, cx);
8791 assert_eq!(
8792 view.selections.display_ranges(cx),
8793 vec![
8794 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8795 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8796 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8797 ]
8798 );
8799 });
8800
8801 view.update(cx, |view, cx| {
8802 view.change_selections(None, cx, |s| {
8803 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8804 });
8805 });
8806 view.update(cx, |view, cx| {
8807 view.add_selection_above(&AddSelectionAbove, cx);
8808 assert_eq!(
8809 view.selections.display_ranges(cx),
8810 vec![
8811 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8812 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8813 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8814 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8815 ]
8816 );
8817 });
8818
8819 view.update(cx, |view, cx| {
8820 view.add_selection_below(&AddSelectionBelow, cx);
8821 assert_eq!(
8822 view.selections.display_ranges(cx),
8823 vec![
8824 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8825 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8826 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8827 ]
8828 );
8829 });
8830 }
8831
8832 #[gpui::test]
8833 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8834 cx.set_global(Settings::test(cx));
8835
8836 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8837 let buffer = MultiBuffer::build_simple(&text, cx);
8838 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8839
8840 view.update(cx, |view, cx| {
8841 view.change_selections(None, cx, |s| {
8842 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8843 });
8844 view.select_next(
8845 &SelectNext {
8846 replace_newest: false,
8847 },
8848 cx,
8849 );
8850 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8851
8852 view.select_next(
8853 &SelectNext {
8854 replace_newest: false,
8855 },
8856 cx,
8857 );
8858 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8859
8860 view.undo_selection(&UndoSelection, cx);
8861 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8862
8863 view.redo_selection(&RedoSelection, cx);
8864 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8865
8866 view.select_next(
8867 &SelectNext {
8868 replace_newest: false,
8869 },
8870 cx,
8871 );
8872 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8873
8874 view.select_next(
8875 &SelectNext {
8876 replace_newest: false,
8877 },
8878 cx,
8879 );
8880 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8881 });
8882 }
8883
8884 #[gpui::test]
8885 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8886 cx.update(|cx| cx.set_global(Settings::test(cx)));
8887 let language = Arc::new(Language::new(
8888 LanguageConfig::default(),
8889 Some(tree_sitter_rust::language()),
8890 ));
8891
8892 let text = r#"
8893 use mod1::mod2::{mod3, mod4};
8894
8895 fn fn_1(param1: bool, param2: &str) {
8896 let var1 = "text";
8897 }
8898 "#
8899 .unindent();
8900
8901 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8902 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8903 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8904 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8905 .await;
8906
8907 view.update(cx, |view, cx| {
8908 view.change_selections(None, cx, |s| {
8909 s.select_display_ranges([
8910 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8911 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8912 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8913 ]);
8914 });
8915 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8916 });
8917 assert_eq!(
8918 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8919 &[
8920 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8921 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8922 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8923 ]
8924 );
8925
8926 view.update(cx, |view, cx| {
8927 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8928 });
8929 assert_eq!(
8930 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8931 &[
8932 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8933 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8934 ]
8935 );
8936
8937 view.update(cx, |view, cx| {
8938 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8939 });
8940 assert_eq!(
8941 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8942 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8943 );
8944
8945 // Trying to expand the selected syntax node one more time has no effect.
8946 view.update(cx, |view, cx| {
8947 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8948 });
8949 assert_eq!(
8950 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8951 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8952 );
8953
8954 view.update(cx, |view, cx| {
8955 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8956 });
8957 assert_eq!(
8958 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8959 &[
8960 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8961 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8962 ]
8963 );
8964
8965 view.update(cx, |view, cx| {
8966 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8967 });
8968 assert_eq!(
8969 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8970 &[
8971 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8972 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8973 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8974 ]
8975 );
8976
8977 view.update(cx, |view, cx| {
8978 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8979 });
8980 assert_eq!(
8981 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8982 &[
8983 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8984 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8985 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8986 ]
8987 );
8988
8989 // Trying to shrink the selected syntax node one more time has no effect.
8990 view.update(cx, |view, cx| {
8991 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8992 });
8993 assert_eq!(
8994 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8995 &[
8996 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8997 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8998 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8999 ]
9000 );
9001
9002 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9003 // a fold.
9004 view.update(cx, |view, cx| {
9005 view.fold_ranges(
9006 vec![
9007 Point::new(0, 21)..Point::new(0, 24),
9008 Point::new(3, 20)..Point::new(3, 22),
9009 ],
9010 cx,
9011 );
9012 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9013 });
9014 assert_eq!(
9015 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9016 &[
9017 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9018 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9019 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9020 ]
9021 );
9022 }
9023
9024 #[gpui::test]
9025 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9026 cx.update(|cx| cx.set_global(Settings::test(cx)));
9027 let language = Arc::new(
9028 Language::new(
9029 LanguageConfig {
9030 brackets: vec![
9031 BracketPair {
9032 start: "{".to_string(),
9033 end: "}".to_string(),
9034 close: false,
9035 newline: true,
9036 },
9037 BracketPair {
9038 start: "(".to_string(),
9039 end: ")".to_string(),
9040 close: false,
9041 newline: true,
9042 },
9043 ],
9044 ..Default::default()
9045 },
9046 Some(tree_sitter_rust::language()),
9047 )
9048 .with_indents_query(
9049 r#"
9050 (_ "(" ")" @end) @indent
9051 (_ "{" "}" @end) @indent
9052 "#,
9053 )
9054 .unwrap(),
9055 );
9056
9057 let text = "fn a() {}";
9058
9059 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9060 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9061 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9062 editor
9063 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9064 .await;
9065
9066 editor.update(cx, |editor, cx| {
9067 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9068 editor.newline(&Newline, cx);
9069 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9070 assert_eq!(
9071 editor.selections.ranges(cx),
9072 &[
9073 Point::new(1, 4)..Point::new(1, 4),
9074 Point::new(3, 4)..Point::new(3, 4),
9075 Point::new(5, 0)..Point::new(5, 0)
9076 ]
9077 );
9078 });
9079 }
9080
9081 #[gpui::test]
9082 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9083 cx.update(|cx| cx.set_global(Settings::test(cx)));
9084 let language = Arc::new(Language::new(
9085 LanguageConfig {
9086 brackets: vec![
9087 BracketPair {
9088 start: "{".to_string(),
9089 end: "}".to_string(),
9090 close: true,
9091 newline: true,
9092 },
9093 BracketPair {
9094 start: "/*".to_string(),
9095 end: " */".to_string(),
9096 close: true,
9097 newline: true,
9098 },
9099 BracketPair {
9100 start: "[".to_string(),
9101 end: "]".to_string(),
9102 close: false,
9103 newline: true,
9104 },
9105 ],
9106 autoclose_before: "})]".to_string(),
9107 ..Default::default()
9108 },
9109 Some(tree_sitter_rust::language()),
9110 ));
9111
9112 let text = r#"
9113 a
9114
9115 /
9116
9117 "#
9118 .unindent();
9119
9120 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9121 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9122 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9123 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9124 .await;
9125
9126 view.update(cx, |view, cx| {
9127 view.change_selections(None, cx, |s| {
9128 s.select_display_ranges([
9129 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9130 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9131 ])
9132 });
9133
9134 view.handle_input("{", cx);
9135 view.handle_input("{", cx);
9136 view.handle_input("{", cx);
9137 assert_eq!(
9138 view.text(cx),
9139 "
9140 {{{}}}
9141 {{{}}}
9142 /
9143
9144 "
9145 .unindent()
9146 );
9147
9148 view.move_right(&MoveRight, cx);
9149 view.handle_input("}", cx);
9150 view.handle_input("}", cx);
9151 view.handle_input("}", cx);
9152 assert_eq!(
9153 view.text(cx),
9154 "
9155 {{{}}}}
9156 {{{}}}}
9157 /
9158
9159 "
9160 .unindent()
9161 );
9162
9163 view.undo(&Undo, cx);
9164 view.handle_input("/", cx);
9165 view.handle_input("*", cx);
9166 assert_eq!(
9167 view.text(cx),
9168 "
9169 /* */
9170 /* */
9171 /
9172
9173 "
9174 .unindent()
9175 );
9176
9177 view.undo(&Undo, cx);
9178 view.change_selections(None, cx, |s| {
9179 s.select_display_ranges([
9180 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9181 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9182 ])
9183 });
9184 view.handle_input("*", cx);
9185 assert_eq!(
9186 view.text(cx),
9187 "
9188 a
9189
9190 /*
9191 *
9192 "
9193 .unindent()
9194 );
9195
9196 // Don't autoclose if the next character isn't whitespace and isn't
9197 // listed in the language's "autoclose_before" section.
9198 view.finalize_last_transaction(cx);
9199 view.change_selections(None, cx, |s| {
9200 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9201 });
9202 view.handle_input("{", cx);
9203 assert_eq!(
9204 view.text(cx),
9205 "
9206 {a
9207
9208 /*
9209 *
9210 "
9211 .unindent()
9212 );
9213
9214 view.undo(&Undo, cx);
9215 view.change_selections(None, cx, |s| {
9216 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9217 });
9218 view.handle_input("{", cx);
9219 assert_eq!(
9220 view.text(cx),
9221 "
9222 {a}
9223
9224 /*
9225 *
9226 "
9227 .unindent()
9228 );
9229 assert_eq!(
9230 view.selections.display_ranges(cx),
9231 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9232 );
9233
9234 view.undo(&Undo, cx);
9235 view.handle_input("[", cx);
9236 assert_eq!(
9237 view.text(cx),
9238 "
9239 [a]
9240
9241 /*
9242 *
9243 "
9244 .unindent()
9245 );
9246 assert_eq!(
9247 view.selections.display_ranges(cx),
9248 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9249 );
9250
9251 view.undo(&Undo, cx);
9252 view.change_selections(None, cx, |s| {
9253 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9254 });
9255 view.handle_input("[", cx);
9256 assert_eq!(
9257 view.text(cx),
9258 "
9259 a[
9260
9261 /*
9262 *
9263 "
9264 .unindent()
9265 );
9266 assert_eq!(
9267 view.selections.display_ranges(cx),
9268 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9269 );
9270 });
9271 }
9272
9273 #[gpui::test]
9274 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9275 cx.update(|cx| cx.set_global(Settings::test(cx)));
9276 let language = Arc::new(Language::new(
9277 LanguageConfig {
9278 brackets: vec![BracketPair {
9279 start: "{".to_string(),
9280 end: "}".to_string(),
9281 close: true,
9282 newline: true,
9283 }],
9284 ..Default::default()
9285 },
9286 Some(tree_sitter_rust::language()),
9287 ));
9288
9289 let text = r#"
9290 a
9291 b
9292 c
9293 "#
9294 .unindent();
9295
9296 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9297 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9298 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9299 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9300 .await;
9301
9302 view.update(cx, |view, cx| {
9303 view.change_selections(None, cx, |s| {
9304 s.select_display_ranges([
9305 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9306 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9307 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9308 ])
9309 });
9310
9311 view.handle_input("{", cx);
9312 view.handle_input("{", cx);
9313 view.handle_input("{", cx);
9314 assert_eq!(
9315 view.text(cx),
9316 "
9317 {{{a}}}
9318 {{{b}}}
9319 {{{c}}}
9320 "
9321 .unindent()
9322 );
9323 assert_eq!(
9324 view.selections.display_ranges(cx),
9325 [
9326 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9327 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9328 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9329 ]
9330 );
9331
9332 view.undo(&Undo, cx);
9333 assert_eq!(
9334 view.text(cx),
9335 "
9336 a
9337 b
9338 c
9339 "
9340 .unindent()
9341 );
9342 assert_eq!(
9343 view.selections.display_ranges(cx),
9344 [
9345 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9346 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9347 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9348 ]
9349 );
9350 });
9351 }
9352
9353 #[gpui::test]
9354 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9355 cx.update(|cx| cx.set_global(Settings::test(cx)));
9356 let language = Arc::new(Language::new(
9357 LanguageConfig {
9358 brackets: vec![BracketPair {
9359 start: "{".to_string(),
9360 end: "}".to_string(),
9361 close: true,
9362 newline: true,
9363 }],
9364 autoclose_before: "}".to_string(),
9365 ..Default::default()
9366 },
9367 Some(tree_sitter_rust::language()),
9368 ));
9369
9370 let text = r#"
9371 a
9372 b
9373 c
9374 "#
9375 .unindent();
9376
9377 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9378 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9379 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9380 editor
9381 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9382 .await;
9383
9384 editor.update(cx, |editor, cx| {
9385 editor.change_selections(None, cx, |s| {
9386 s.select_ranges([
9387 Point::new(0, 1)..Point::new(0, 1),
9388 Point::new(1, 1)..Point::new(1, 1),
9389 Point::new(2, 1)..Point::new(2, 1),
9390 ])
9391 });
9392
9393 editor.handle_input("{", cx);
9394 editor.handle_input("{", cx);
9395 editor.handle_input("_", cx);
9396 assert_eq!(
9397 editor.text(cx),
9398 "
9399 a{{_}}
9400 b{{_}}
9401 c{{_}}
9402 "
9403 .unindent()
9404 );
9405 assert_eq!(
9406 editor.selections.ranges::<Point>(cx),
9407 [
9408 Point::new(0, 4)..Point::new(0, 4),
9409 Point::new(1, 4)..Point::new(1, 4),
9410 Point::new(2, 4)..Point::new(2, 4)
9411 ]
9412 );
9413
9414 editor.backspace(&Default::default(), cx);
9415 editor.backspace(&Default::default(), cx);
9416 assert_eq!(
9417 editor.text(cx),
9418 "
9419 a{}
9420 b{}
9421 c{}
9422 "
9423 .unindent()
9424 );
9425 assert_eq!(
9426 editor.selections.ranges::<Point>(cx),
9427 [
9428 Point::new(0, 2)..Point::new(0, 2),
9429 Point::new(1, 2)..Point::new(1, 2),
9430 Point::new(2, 2)..Point::new(2, 2)
9431 ]
9432 );
9433
9434 editor.delete_to_previous_word_start(&Default::default(), cx);
9435 assert_eq!(
9436 editor.text(cx),
9437 "
9438 a
9439 b
9440 c
9441 "
9442 .unindent()
9443 );
9444 assert_eq!(
9445 editor.selections.ranges::<Point>(cx),
9446 [
9447 Point::new(0, 1)..Point::new(0, 1),
9448 Point::new(1, 1)..Point::new(1, 1),
9449 Point::new(2, 1)..Point::new(2, 1)
9450 ]
9451 );
9452 });
9453 }
9454
9455 #[gpui::test]
9456 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9457 cx.update(|cx| cx.set_global(Settings::test(cx)));
9458
9459 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9460 a.| b
9461 a.| b
9462 a.| b"});
9463 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9464 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9465
9466 editor.update(cx, |editor, cx| {
9467 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9468
9469 editor
9470 .insert_snippet(&insertion_ranges, snippet, cx)
9471 .unwrap();
9472
9473 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9474 let range_markers = ('<', '>');
9475 let (expected_text, mut selection_ranges_lookup) =
9476 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9477 let selection_ranges = selection_ranges_lookup
9478 .remove(&range_markers.into())
9479 .unwrap();
9480 assert_eq!(editor.text(cx), expected_text);
9481 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9482 }
9483 assert(
9484 editor,
9485 cx,
9486 indoc! {"
9487 a.f(<one>, two, <three>) b
9488 a.f(<one>, two, <three>) b
9489 a.f(<one>, two, <three>) b"},
9490 );
9491
9492 // Can't move earlier than the first tab stop
9493 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9494 assert(
9495 editor,
9496 cx,
9497 indoc! {"
9498 a.f(<one>, two, <three>) b
9499 a.f(<one>, two, <three>) b
9500 a.f(<one>, two, <three>) b"},
9501 );
9502
9503 assert!(editor.move_to_next_snippet_tabstop(cx));
9504 assert(
9505 editor,
9506 cx,
9507 indoc! {"
9508 a.f(one, <two>, three) b
9509 a.f(one, <two>, three) b
9510 a.f(one, <two>, three) b"},
9511 );
9512
9513 editor.move_to_prev_snippet_tabstop(cx);
9514 assert(
9515 editor,
9516 cx,
9517 indoc! {"
9518 a.f(<one>, two, <three>) b
9519 a.f(<one>, two, <three>) b
9520 a.f(<one>, two, <three>) b"},
9521 );
9522
9523 assert!(editor.move_to_next_snippet_tabstop(cx));
9524 assert(
9525 editor,
9526 cx,
9527 indoc! {"
9528 a.f(one, <two>, three) b
9529 a.f(one, <two>, three) b
9530 a.f(one, <two>, three) b"},
9531 );
9532 assert!(editor.move_to_next_snippet_tabstop(cx));
9533 assert(
9534 editor,
9535 cx,
9536 indoc! {"
9537 a.f(one, two, three)<> b
9538 a.f(one, two, three)<> b
9539 a.f(one, two, three)<> b"},
9540 );
9541
9542 // As soon as the last tab stop is reached, snippet state is gone
9543 editor.move_to_prev_snippet_tabstop(cx);
9544 assert(
9545 editor,
9546 cx,
9547 indoc! {"
9548 a.f(one, two, three)<> b
9549 a.f(one, two, three)<> b
9550 a.f(one, two, three)<> b"},
9551 );
9552 });
9553 }
9554
9555 #[gpui::test]
9556 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9557 cx.foreground().forbid_parking();
9558
9559 let mut language = Language::new(
9560 LanguageConfig {
9561 name: "Rust".into(),
9562 path_suffixes: vec!["rs".to_string()],
9563 ..Default::default()
9564 },
9565 Some(tree_sitter_rust::language()),
9566 );
9567 let mut fake_servers = language
9568 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9569 capabilities: lsp::ServerCapabilities {
9570 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9571 ..Default::default()
9572 },
9573 ..Default::default()
9574 }))
9575 .await;
9576
9577 let fs = FakeFs::new(cx.background().clone());
9578 fs.insert_file("/file.rs", Default::default()).await;
9579
9580 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9581 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9582 let buffer = project
9583 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9584 .await
9585 .unwrap();
9586
9587 cx.foreground().start_waiting();
9588 let fake_server = fake_servers.next().await.unwrap();
9589
9590 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9591 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9592 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9593 assert!(cx.read(|cx| editor.is_dirty(cx)));
9594
9595 let save = cx.update(|cx| editor.save(project.clone(), cx));
9596 fake_server
9597 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9598 assert_eq!(
9599 params.text_document.uri,
9600 lsp::Url::from_file_path("/file.rs").unwrap()
9601 );
9602 assert_eq!(params.options.tab_size, 4);
9603 Ok(Some(vec![lsp::TextEdit::new(
9604 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9605 ", ".to_string(),
9606 )]))
9607 })
9608 .next()
9609 .await;
9610 cx.foreground().start_waiting();
9611 save.await.unwrap();
9612 assert_eq!(
9613 editor.read_with(cx, |editor, cx| editor.text(cx)),
9614 "one, two\nthree\n"
9615 );
9616 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9617
9618 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9619 assert!(cx.read(|cx| editor.is_dirty(cx)));
9620
9621 // Ensure we can still save even if formatting hangs.
9622 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9623 assert_eq!(
9624 params.text_document.uri,
9625 lsp::Url::from_file_path("/file.rs").unwrap()
9626 );
9627 futures::future::pending::<()>().await;
9628 unreachable!()
9629 });
9630 let save = cx.update(|cx| editor.save(project.clone(), cx));
9631 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9632 cx.foreground().start_waiting();
9633 save.await.unwrap();
9634 assert_eq!(
9635 editor.read_with(cx, |editor, cx| editor.text(cx)),
9636 "one\ntwo\nthree\n"
9637 );
9638 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9639
9640 // Set rust language override and assert overriden tabsize is sent to language server
9641 cx.update(|cx| {
9642 cx.update_global::<Settings, _, _>(|settings, _| {
9643 settings.language_overrides.insert(
9644 "Rust".into(),
9645 EditorSettings {
9646 tab_size: Some(8.try_into().unwrap()),
9647 ..Default::default()
9648 },
9649 );
9650 })
9651 });
9652
9653 let save = cx.update(|cx| editor.save(project.clone(), cx));
9654 fake_server
9655 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9656 assert_eq!(
9657 params.text_document.uri,
9658 lsp::Url::from_file_path("/file.rs").unwrap()
9659 );
9660 assert_eq!(params.options.tab_size, 8);
9661 Ok(Some(vec![]))
9662 })
9663 .next()
9664 .await;
9665 cx.foreground().start_waiting();
9666 save.await.unwrap();
9667 }
9668
9669 #[gpui::test]
9670 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9671 cx.foreground().forbid_parking();
9672
9673 let mut language = Language::new(
9674 LanguageConfig {
9675 name: "Rust".into(),
9676 path_suffixes: vec!["rs".to_string()],
9677 ..Default::default()
9678 },
9679 Some(tree_sitter_rust::language()),
9680 );
9681 let mut fake_servers = language
9682 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9683 capabilities: lsp::ServerCapabilities {
9684 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9685 ..Default::default()
9686 },
9687 ..Default::default()
9688 }))
9689 .await;
9690
9691 let fs = FakeFs::new(cx.background().clone());
9692 fs.insert_file("/file.rs", Default::default()).await;
9693
9694 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9695 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9696 let buffer = project
9697 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9698 .await
9699 .unwrap();
9700
9701 cx.foreground().start_waiting();
9702 let fake_server = fake_servers.next().await.unwrap();
9703
9704 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9705 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9706 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9707 assert!(cx.read(|cx| editor.is_dirty(cx)));
9708
9709 let save = cx.update(|cx| editor.save(project.clone(), cx));
9710 fake_server
9711 .handle_request::<lsp::request::RangeFormatting, _, _>(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, 4);
9717 Ok(Some(vec![lsp::TextEdit::new(
9718 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9719 ", ".to_string(),
9720 )]))
9721 })
9722 .next()
9723 .await;
9724 cx.foreground().start_waiting();
9725 save.await.unwrap();
9726 assert_eq!(
9727 editor.read_with(cx, |editor, cx| editor.text(cx)),
9728 "one, two\nthree\n"
9729 );
9730 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9731
9732 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9733 assert!(cx.read(|cx| editor.is_dirty(cx)));
9734
9735 // Ensure we can still save even if formatting hangs.
9736 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9737 move |params, _| async move {
9738 assert_eq!(
9739 params.text_document.uri,
9740 lsp::Url::from_file_path("/file.rs").unwrap()
9741 );
9742 futures::future::pending::<()>().await;
9743 unreachable!()
9744 },
9745 );
9746 let save = cx.update(|cx| editor.save(project.clone(), cx));
9747 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9748 cx.foreground().start_waiting();
9749 save.await.unwrap();
9750 assert_eq!(
9751 editor.read_with(cx, |editor, cx| editor.text(cx)),
9752 "one\ntwo\nthree\n"
9753 );
9754 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9755
9756 // Set rust language override and assert overriden tabsize is sent to language server
9757 cx.update(|cx| {
9758 cx.update_global::<Settings, _, _>(|settings, _| {
9759 settings.language_overrides.insert(
9760 "Rust".into(),
9761 EditorSettings {
9762 tab_size: Some(8.try_into().unwrap()),
9763 ..Default::default()
9764 },
9765 );
9766 })
9767 });
9768
9769 let save = cx.update(|cx| editor.save(project.clone(), cx));
9770 fake_server
9771 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9772 assert_eq!(
9773 params.text_document.uri,
9774 lsp::Url::from_file_path("/file.rs").unwrap()
9775 );
9776 assert_eq!(params.options.tab_size, 8);
9777 Ok(Some(vec![]))
9778 })
9779 .next()
9780 .await;
9781 cx.foreground().start_waiting();
9782 save.await.unwrap();
9783 }
9784
9785 #[gpui::test]
9786 async fn test_completion(cx: &mut gpui::TestAppContext) {
9787 let mut cx = EditorLspTestContext::new_rust(
9788 lsp::ServerCapabilities {
9789 completion_provider: Some(lsp::CompletionOptions {
9790 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9791 ..Default::default()
9792 }),
9793 ..Default::default()
9794 },
9795 cx,
9796 )
9797 .await;
9798
9799 cx.set_state(indoc! {"
9800 one|
9801 two
9802 three"});
9803 cx.simulate_input(".");
9804 handle_completion_request(
9805 &mut cx,
9806 indoc! {"
9807 one.|<>
9808 two
9809 three"},
9810 vec!["first_completion", "second_completion"],
9811 )
9812 .await;
9813 cx.condition(|editor, _| editor.context_menu_visible())
9814 .await;
9815 let apply_additional_edits = cx.update_editor(|editor, cx| {
9816 editor.move_down(&MoveDown, cx);
9817 editor
9818 .confirm_completion(&ConfirmCompletion::default(), cx)
9819 .unwrap()
9820 });
9821 cx.assert_editor_state(indoc! {"
9822 one.second_completion|
9823 two
9824 three"});
9825
9826 handle_resolve_completion_request(
9827 &mut cx,
9828 Some((
9829 indoc! {"
9830 one.second_completion
9831 two
9832 three<>"},
9833 "\nadditional edit",
9834 )),
9835 )
9836 .await;
9837 apply_additional_edits.await.unwrap();
9838 cx.assert_editor_state(indoc! {"
9839 one.second_completion|
9840 two
9841 three
9842 additional edit"});
9843
9844 cx.set_state(indoc! {"
9845 one.second_completion
9846 two|
9847 three|
9848 additional edit"});
9849 cx.simulate_input(" ");
9850 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9851 cx.simulate_input("s");
9852 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9853
9854 cx.assert_editor_state(indoc! {"
9855 one.second_completion
9856 two s|
9857 three s|
9858 additional edit"});
9859 handle_completion_request(
9860 &mut cx,
9861 indoc! {"
9862 one.second_completion
9863 two s
9864 three <s|>
9865 additional edit"},
9866 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9867 )
9868 .await;
9869 cx.condition(|editor, _| editor.context_menu_visible())
9870 .await;
9871
9872 cx.simulate_input("i");
9873
9874 handle_completion_request(
9875 &mut cx,
9876 indoc! {"
9877 one.second_completion
9878 two si
9879 three <si|>
9880 additional edit"},
9881 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9882 )
9883 .await;
9884 cx.condition(|editor, _| editor.context_menu_visible())
9885 .await;
9886
9887 let apply_additional_edits = cx.update_editor(|editor, cx| {
9888 editor
9889 .confirm_completion(&ConfirmCompletion::default(), cx)
9890 .unwrap()
9891 });
9892 cx.assert_editor_state(indoc! {"
9893 one.second_completion
9894 two sixth_completion|
9895 three sixth_completion|
9896 additional edit"});
9897
9898 handle_resolve_completion_request(&mut cx, None).await;
9899 apply_additional_edits.await.unwrap();
9900
9901 cx.update(|cx| {
9902 cx.update_global::<Settings, _, _>(|settings, _| {
9903 settings.show_completions_on_input = false;
9904 })
9905 });
9906 cx.set_state("editor|");
9907 cx.simulate_input(".");
9908 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9909 cx.simulate_input("c");
9910 cx.simulate_input("l");
9911 cx.simulate_input("o");
9912 cx.assert_editor_state("editor.clo|");
9913 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9914 cx.update_editor(|editor, cx| {
9915 editor.show_completions(&ShowCompletions, cx);
9916 });
9917 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
9918 cx.condition(|editor, _| editor.context_menu_visible())
9919 .await;
9920 let apply_additional_edits = cx.update_editor(|editor, cx| {
9921 editor
9922 .confirm_completion(&ConfirmCompletion::default(), cx)
9923 .unwrap()
9924 });
9925 cx.assert_editor_state("editor.close|");
9926 handle_resolve_completion_request(&mut cx, None).await;
9927 apply_additional_edits.await.unwrap();
9928
9929 // Handle completion request passing a marked string specifying where the completion
9930 // should be triggered from using '|' character, what range should be replaced, and what completions
9931 // should be returned using '<' and '>' to delimit the range
9932 async fn handle_completion_request<'a>(
9933 cx: &mut EditorLspTestContext<'a>,
9934 marked_string: &str,
9935 completions: Vec<&'static str>,
9936 ) {
9937 let complete_from_marker: TextRangeMarker = '|'.into();
9938 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9939 let (_, mut marked_ranges) = marked_text_ranges_by(
9940 marked_string,
9941 vec![complete_from_marker.clone(), replace_range_marker.clone()],
9942 );
9943
9944 let complete_from_position =
9945 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9946 let replace_range =
9947 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9948
9949 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9950 let completions = completions.clone();
9951 async move {
9952 assert_eq!(params.text_document_position.text_document.uri, url.clone());
9953 assert_eq!(
9954 params.text_document_position.position,
9955 complete_from_position
9956 );
9957 Ok(Some(lsp::CompletionResponse::Array(
9958 completions
9959 .iter()
9960 .map(|completion_text| lsp::CompletionItem {
9961 label: completion_text.to_string(),
9962 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9963 range: replace_range.clone(),
9964 new_text: completion_text.to_string(),
9965 })),
9966 ..Default::default()
9967 })
9968 .collect(),
9969 )))
9970 }
9971 })
9972 .next()
9973 .await;
9974 }
9975
9976 async fn handle_resolve_completion_request<'a>(
9977 cx: &mut EditorLspTestContext<'a>,
9978 edit: Option<(&'static str, &'static str)>,
9979 ) {
9980 let edit = edit.map(|(marked_string, new_text)| {
9981 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9982 let (_, mut marked_ranges) =
9983 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
9984
9985 let replace_range = cx
9986 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9987
9988 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
9989 });
9990
9991 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9992 let edit = edit.clone();
9993 async move {
9994 Ok(lsp::CompletionItem {
9995 additional_text_edits: edit,
9996 ..Default::default()
9997 })
9998 }
9999 })
10000 .next()
10001 .await;
10002 }
10003 }
10004
10005 #[gpui::test]
10006 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10007 cx.update(|cx| cx.set_global(Settings::test(cx)));
10008 let language = Arc::new(Language::new(
10009 LanguageConfig {
10010 line_comment: Some("// ".to_string()),
10011 ..Default::default()
10012 },
10013 Some(tree_sitter_rust::language()),
10014 ));
10015
10016 let text = "
10017 fn a() {
10018 //b();
10019 // c();
10020 // d();
10021 }
10022 "
10023 .unindent();
10024
10025 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10026 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10027 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10028
10029 view.update(cx, |editor, cx| {
10030 // If multiple selections intersect a line, the line is only
10031 // toggled once.
10032 editor.change_selections(None, cx, |s| {
10033 s.select_display_ranges([
10034 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10035 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10036 ])
10037 });
10038 editor.toggle_comments(&ToggleComments, cx);
10039 assert_eq!(
10040 editor.text(cx),
10041 "
10042 fn a() {
10043 b();
10044 c();
10045 d();
10046 }
10047 "
10048 .unindent()
10049 );
10050
10051 // The comment prefix is inserted at the same column for every line
10052 // in a selection.
10053 editor.change_selections(None, cx, |s| {
10054 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10055 });
10056 editor.toggle_comments(&ToggleComments, cx);
10057 assert_eq!(
10058 editor.text(cx),
10059 "
10060 fn a() {
10061 // b();
10062 // c();
10063 // d();
10064 }
10065 "
10066 .unindent()
10067 );
10068
10069 // If a selection ends at the beginning of a line, that line is not toggled.
10070 editor.change_selections(None, cx, |s| {
10071 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10072 });
10073 editor.toggle_comments(&ToggleComments, cx);
10074 assert_eq!(
10075 editor.text(cx),
10076 "
10077 fn a() {
10078 // b();
10079 c();
10080 // d();
10081 }
10082 "
10083 .unindent()
10084 );
10085 });
10086 }
10087
10088 #[gpui::test]
10089 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10090 cx.set_global(Settings::test(cx));
10091 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10092 let multibuffer = cx.add_model(|cx| {
10093 let mut multibuffer = MultiBuffer::new(0);
10094 multibuffer.push_excerpts(
10095 buffer.clone(),
10096 [
10097 ExcerptRange {
10098 context: Point::new(0, 0)..Point::new(0, 4),
10099 primary: None,
10100 },
10101 ExcerptRange {
10102 context: Point::new(1, 0)..Point::new(1, 4),
10103 primary: None,
10104 },
10105 ],
10106 cx,
10107 );
10108 multibuffer
10109 });
10110
10111 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10112
10113 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10114 view.update(cx, |view, cx| {
10115 assert_eq!(view.text(cx), "aaaa\nbbbb");
10116 view.change_selections(None, cx, |s| {
10117 s.select_ranges([
10118 Point::new(0, 0)..Point::new(0, 0),
10119 Point::new(1, 0)..Point::new(1, 0),
10120 ])
10121 });
10122
10123 view.handle_input("X", cx);
10124 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10125 assert_eq!(
10126 view.selections.ranges(cx),
10127 [
10128 Point::new(0, 1)..Point::new(0, 1),
10129 Point::new(1, 1)..Point::new(1, 1),
10130 ]
10131 )
10132 });
10133 }
10134
10135 #[gpui::test]
10136 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10137 cx.set_global(Settings::test(cx));
10138 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10139 [aaaa
10140 (bbbb]
10141 cccc)"});
10142 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10143 context,
10144 primary: None,
10145 });
10146 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10147 let multibuffer = cx.add_model(|cx| {
10148 let mut multibuffer = MultiBuffer::new(0);
10149 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10150 multibuffer
10151 });
10152
10153 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10154 view.update(cx, |view, cx| {
10155 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10156 aaaa
10157 b|bbb
10158 b|bb|b
10159 cccc"});
10160 assert_eq!(view.text(cx), expected_text);
10161 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10162
10163 view.handle_input("X", cx);
10164
10165 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10166 aaaa
10167 bX|bbXb
10168 bX|bbX|b
10169 cccc"});
10170 assert_eq!(view.text(cx), expected_text);
10171 assert_eq!(view.selections.ranges(cx), expected_selections);
10172
10173 view.newline(&Newline, cx);
10174 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10175 aaaa
10176 bX
10177 |bbX
10178 b
10179 bX
10180 |bbX
10181 |b
10182 cccc"});
10183 assert_eq!(view.text(cx), expected_text);
10184 assert_eq!(view.selections.ranges(cx), expected_selections);
10185 });
10186 }
10187
10188 #[gpui::test]
10189 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10190 cx.set_global(Settings::test(cx));
10191 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10192 let mut excerpt1_id = None;
10193 let multibuffer = cx.add_model(|cx| {
10194 let mut multibuffer = MultiBuffer::new(0);
10195 excerpt1_id = multibuffer
10196 .push_excerpts(
10197 buffer.clone(),
10198 [
10199 ExcerptRange {
10200 context: Point::new(0, 0)..Point::new(1, 4),
10201 primary: None,
10202 },
10203 ExcerptRange {
10204 context: Point::new(1, 0)..Point::new(2, 4),
10205 primary: None,
10206 },
10207 ],
10208 cx,
10209 )
10210 .into_iter()
10211 .next();
10212 multibuffer
10213 });
10214 assert_eq!(
10215 multibuffer.read(cx).read(cx).text(),
10216 "aaaa\nbbbb\nbbbb\ncccc"
10217 );
10218 let (_, editor) = cx.add_window(Default::default(), |cx| {
10219 let mut editor = build_editor(multibuffer.clone(), cx);
10220 let snapshot = editor.snapshot(cx);
10221 editor.change_selections(None, cx, |s| {
10222 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10223 });
10224 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10225 assert_eq!(
10226 editor.selections.ranges(cx),
10227 [
10228 Point::new(1, 3)..Point::new(1, 3),
10229 Point::new(2, 1)..Point::new(2, 1),
10230 ]
10231 );
10232 editor
10233 });
10234
10235 // Refreshing selections is a no-op when excerpts haven't changed.
10236 editor.update(cx, |editor, cx| {
10237 editor.change_selections(None, cx, |s| {
10238 s.refresh();
10239 });
10240 assert_eq!(
10241 editor.selections.ranges(cx),
10242 [
10243 Point::new(1, 3)..Point::new(1, 3),
10244 Point::new(2, 1)..Point::new(2, 1),
10245 ]
10246 );
10247 });
10248
10249 multibuffer.update(cx, |multibuffer, cx| {
10250 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10251 });
10252 editor.update(cx, |editor, cx| {
10253 // Removing an excerpt causes the first selection to become degenerate.
10254 assert_eq!(
10255 editor.selections.ranges(cx),
10256 [
10257 Point::new(0, 0)..Point::new(0, 0),
10258 Point::new(0, 1)..Point::new(0, 1)
10259 ]
10260 );
10261
10262 // Refreshing selections will relocate the first selection to the original buffer
10263 // location.
10264 editor.change_selections(None, cx, |s| {
10265 s.refresh();
10266 });
10267 assert_eq!(
10268 editor.selections.ranges(cx),
10269 [
10270 Point::new(0, 1)..Point::new(0, 1),
10271 Point::new(0, 3)..Point::new(0, 3)
10272 ]
10273 );
10274 assert!(editor.selections.pending_anchor().is_some());
10275 });
10276 }
10277
10278 #[gpui::test]
10279 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10280 cx.set_global(Settings::test(cx));
10281 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10282 let mut excerpt1_id = None;
10283 let multibuffer = cx.add_model(|cx| {
10284 let mut multibuffer = MultiBuffer::new(0);
10285 excerpt1_id = multibuffer
10286 .push_excerpts(
10287 buffer.clone(),
10288 [
10289 ExcerptRange {
10290 context: Point::new(0, 0)..Point::new(1, 4),
10291 primary: None,
10292 },
10293 ExcerptRange {
10294 context: Point::new(1, 0)..Point::new(2, 4),
10295 primary: None,
10296 },
10297 ],
10298 cx,
10299 )
10300 .into_iter()
10301 .next();
10302 multibuffer
10303 });
10304 assert_eq!(
10305 multibuffer.read(cx).read(cx).text(),
10306 "aaaa\nbbbb\nbbbb\ncccc"
10307 );
10308 let (_, editor) = cx.add_window(Default::default(), |cx| {
10309 let mut editor = build_editor(multibuffer.clone(), cx);
10310 let snapshot = editor.snapshot(cx);
10311 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10312 assert_eq!(
10313 editor.selections.ranges(cx),
10314 [Point::new(1, 3)..Point::new(1, 3)]
10315 );
10316 editor
10317 });
10318
10319 multibuffer.update(cx, |multibuffer, cx| {
10320 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10321 });
10322 editor.update(cx, |editor, cx| {
10323 assert_eq!(
10324 editor.selections.ranges(cx),
10325 [Point::new(0, 0)..Point::new(0, 0)]
10326 );
10327
10328 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10329 editor.change_selections(None, cx, |s| {
10330 s.refresh();
10331 });
10332 assert_eq!(
10333 editor.selections.ranges(cx),
10334 [Point::new(0, 3)..Point::new(0, 3)]
10335 );
10336 assert!(editor.selections.pending_anchor().is_some());
10337 });
10338 }
10339
10340 #[gpui::test]
10341 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10342 cx.update(|cx| cx.set_global(Settings::test(cx)));
10343 let language = Arc::new(
10344 Language::new(
10345 LanguageConfig {
10346 brackets: vec![
10347 BracketPair {
10348 start: "{".to_string(),
10349 end: "}".to_string(),
10350 close: true,
10351 newline: true,
10352 },
10353 BracketPair {
10354 start: "/* ".to_string(),
10355 end: " */".to_string(),
10356 close: true,
10357 newline: true,
10358 },
10359 ],
10360 ..Default::default()
10361 },
10362 Some(tree_sitter_rust::language()),
10363 )
10364 .with_indents_query("")
10365 .unwrap(),
10366 );
10367
10368 let text = concat!(
10369 "{ }\n", // Suppress rustfmt
10370 " x\n", //
10371 " /* */\n", //
10372 "x\n", //
10373 "{{} }\n", //
10374 );
10375
10376 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10377 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10378 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10379 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10380 .await;
10381
10382 view.update(cx, |view, cx| {
10383 view.change_selections(None, cx, |s| {
10384 s.select_display_ranges([
10385 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10386 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10387 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10388 ])
10389 });
10390 view.newline(&Newline, cx);
10391
10392 assert_eq!(
10393 view.buffer().read(cx).read(cx).text(),
10394 concat!(
10395 "{ \n", // Suppress rustfmt
10396 "\n", //
10397 "}\n", //
10398 " x\n", //
10399 " /* \n", //
10400 " \n", //
10401 " */\n", //
10402 "x\n", //
10403 "{{} \n", //
10404 "}\n", //
10405 )
10406 );
10407 });
10408 }
10409
10410 #[gpui::test]
10411 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10412 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10413
10414 cx.set_global(Settings::test(cx));
10415 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10416
10417 editor.update(cx, |editor, cx| {
10418 struct Type1;
10419 struct Type2;
10420
10421 let buffer = buffer.read(cx).snapshot(cx);
10422
10423 let anchor_range = |range: Range<Point>| {
10424 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10425 };
10426
10427 editor.highlight_background::<Type1>(
10428 vec![
10429 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10430 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10431 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10432 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10433 ],
10434 |_| Color::red(),
10435 cx,
10436 );
10437 editor.highlight_background::<Type2>(
10438 vec![
10439 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10440 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10441 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10442 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10443 ],
10444 |_| Color::green(),
10445 cx,
10446 );
10447
10448 let snapshot = editor.snapshot(cx);
10449 let mut highlighted_ranges = editor.background_highlights_in_range(
10450 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10451 &snapshot,
10452 cx.global::<Settings>().theme.as_ref(),
10453 );
10454 // Enforce a consistent ordering based on color without relying on the ordering of the
10455 // highlight's `TypeId` which is non-deterministic.
10456 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10457 assert_eq!(
10458 highlighted_ranges,
10459 &[
10460 (
10461 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10462 Color::green(),
10463 ),
10464 (
10465 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10466 Color::green(),
10467 ),
10468 (
10469 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10470 Color::red(),
10471 ),
10472 (
10473 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10474 Color::red(),
10475 ),
10476 ]
10477 );
10478 assert_eq!(
10479 editor.background_highlights_in_range(
10480 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10481 &snapshot,
10482 cx.global::<Settings>().theme.as_ref(),
10483 ),
10484 &[(
10485 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10486 Color::red(),
10487 )]
10488 );
10489 });
10490 }
10491
10492 #[gpui::test]
10493 fn test_following(cx: &mut gpui::MutableAppContext) {
10494 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10495
10496 cx.set_global(Settings::test(cx));
10497
10498 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10499 let (_, follower) = cx.add_window(
10500 WindowOptions {
10501 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10502 ..Default::default()
10503 },
10504 |cx| build_editor(buffer.clone(), cx),
10505 );
10506
10507 let pending_update = Rc::new(RefCell::new(None));
10508 follower.update(cx, {
10509 let update = pending_update.clone();
10510 |_, cx| {
10511 cx.subscribe(&leader, move |_, leader, event, cx| {
10512 leader
10513 .read(cx)
10514 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10515 })
10516 .detach();
10517 }
10518 });
10519
10520 // Update the selections only
10521 leader.update(cx, |leader, cx| {
10522 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10523 });
10524 follower.update(cx, |follower, cx| {
10525 follower
10526 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10527 .unwrap();
10528 });
10529 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10530
10531 // Update the scroll position only
10532 leader.update(cx, |leader, cx| {
10533 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10534 });
10535 follower.update(cx, |follower, cx| {
10536 follower
10537 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10538 .unwrap();
10539 });
10540 assert_eq!(
10541 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10542 vec2f(1.5, 3.5)
10543 );
10544
10545 // Update the selections and scroll position
10546 leader.update(cx, |leader, cx| {
10547 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10548 leader.request_autoscroll(Autoscroll::Newest, cx);
10549 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10550 });
10551 follower.update(cx, |follower, cx| {
10552 let initial_scroll_position = follower.scroll_position(cx);
10553 follower
10554 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10555 .unwrap();
10556 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10557 assert!(follower.autoscroll_request.is_some());
10558 });
10559 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10560
10561 // Creating a pending selection that precedes another selection
10562 leader.update(cx, |leader, cx| {
10563 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10564 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10565 });
10566 follower.update(cx, |follower, cx| {
10567 follower
10568 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10569 .unwrap();
10570 });
10571 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10572
10573 // Extend the pending selection so that it surrounds another selection
10574 leader.update(cx, |leader, cx| {
10575 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10576 });
10577 follower.update(cx, |follower, cx| {
10578 follower
10579 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10580 .unwrap();
10581 });
10582 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10583 }
10584
10585 #[test]
10586 fn test_combine_syntax_and_fuzzy_match_highlights() {
10587 let string = "abcdefghijklmnop";
10588 let syntax_ranges = [
10589 (
10590 0..3,
10591 HighlightStyle {
10592 color: Some(Color::red()),
10593 ..Default::default()
10594 },
10595 ),
10596 (
10597 4..8,
10598 HighlightStyle {
10599 color: Some(Color::green()),
10600 ..Default::default()
10601 },
10602 ),
10603 ];
10604 let match_indices = [4, 6, 7, 8];
10605 assert_eq!(
10606 combine_syntax_and_fuzzy_match_highlights(
10607 &string,
10608 Default::default(),
10609 syntax_ranges.into_iter(),
10610 &match_indices,
10611 ),
10612 &[
10613 (
10614 0..3,
10615 HighlightStyle {
10616 color: Some(Color::red()),
10617 ..Default::default()
10618 },
10619 ),
10620 (
10621 4..5,
10622 HighlightStyle {
10623 color: Some(Color::green()),
10624 weight: Some(fonts::Weight::BOLD),
10625 ..Default::default()
10626 },
10627 ),
10628 (
10629 5..6,
10630 HighlightStyle {
10631 color: Some(Color::green()),
10632 ..Default::default()
10633 },
10634 ),
10635 (
10636 6..8,
10637 HighlightStyle {
10638 color: Some(Color::green()),
10639 weight: Some(fonts::Weight::BOLD),
10640 ..Default::default()
10641 },
10642 ),
10643 (
10644 8..9,
10645 HighlightStyle {
10646 weight: Some(fonts::Weight::BOLD),
10647 ..Default::default()
10648 },
10649 ),
10650 ]
10651 );
10652 }
10653
10654 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10655 let point = DisplayPoint::new(row as u32, column as u32);
10656 point..point
10657 }
10658
10659 fn assert_selection_ranges(
10660 marked_text: &str,
10661 selection_marker_pairs: Vec<(char, char)>,
10662 view: &mut Editor,
10663 cx: &mut ViewContext<Editor>,
10664 ) {
10665 let snapshot = view.snapshot(cx).display_snapshot;
10666 let mut marker_chars = Vec::new();
10667 for (start, end) in selection_marker_pairs.iter() {
10668 marker_chars.push(*start);
10669 marker_chars.push(*end);
10670 }
10671 let (_, markers) = marked_text_by(marked_text, marker_chars);
10672 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10673 .iter()
10674 .map(|(start, end)| {
10675 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10676 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10677 start..end
10678 })
10679 .collect();
10680 assert_eq!(
10681 view.selections.display_ranges(cx),
10682 &asserted_ranges[..],
10683 "Assert selections are {}",
10684 marked_text
10685 );
10686 }
10687}
10688
10689trait RangeExt<T> {
10690 fn sorted(&self) -> Range<T>;
10691 fn to_inclusive(&self) -> RangeInclusive<T>;
10692}
10693
10694impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10695 fn sorted(&self) -> Self {
10696 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10697 }
10698
10699 fn to_inclusive(&self) -> RangeInclusive<T> {
10700 self.start.clone()..=self.end.clone()
10701 }
10702}
10703
10704trait RangeToAnchorExt {
10705 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10706}
10707
10708impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10709 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10710 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10711 }
10712}