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