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