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