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 tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
3130 let char_column = buffer
3131 .read(cx)
3132 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
3133 .flat_map(str::chars)
3134 .count();
3135 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3136 buffer.edit(
3137 [(
3138 selection.start..selection.start,
3139 " ".repeat(chars_to_next_tab_stop as usize),
3140 )],
3141 cx,
3142 );
3143 selection.start.column += chars_to_next_tab_stop;
3144 selection.end = selection.start;
3145 }
3146 });
3147 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3148 s.select(selections);
3149 });
3150 });
3151 } else {
3152 self.indent(&Indent, cx);
3153 }
3154 }
3155
3156 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3157 let mut selections = self.selections.all::<Point>(cx);
3158 self.transact(cx, |this, cx| {
3159 let mut last_indent = None;
3160 this.buffer.update(cx, |buffer, cx| {
3161 let snapshot = buffer.snapshot(cx);
3162 for selection in &mut selections {
3163 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3164 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
3165 let mut start_row = selection.start.row;
3166 let mut end_row = selection.end.row + 1;
3167
3168 // If a selection ends at the beginning of a line, don't indent
3169 // that last line.
3170 if selection.end.column == 0 {
3171 end_row -= 1;
3172 }
3173
3174 // Avoid re-indenting a row that has already been indented by a
3175 // previous selection, but still update this selection's column
3176 // to reflect that indentation.
3177 if let Some((last_indent_row, last_indent_len)) = last_indent {
3178 if last_indent_row == selection.start.row {
3179 selection.start.column += last_indent_len;
3180 start_row += 1;
3181 }
3182 if last_indent_row == selection.end.row {
3183 selection.end.column += last_indent_len;
3184 }
3185 }
3186
3187 for row in start_row..end_row {
3188 let current_indent = snapshot.indent_size_for_line(row);
3189 let indent_delta = match current_indent.kind {
3190 IndentKind::Space => {
3191 let columns_to_next_tab_stop =
3192 tab_size - (current_indent.len % tab_size);
3193 IndentSize::spaces(columns_to_next_tab_stop)
3194 }
3195 IndentKind::Tab => IndentSize::tab(),
3196 };
3197 let row_start = Point::new(row, 0);
3198 buffer.edit(
3199 [(
3200 row_start..row_start,
3201 indent_delta.chars().collect::<String>(),
3202 )],
3203 cx,
3204 );
3205
3206 // Update this selection's endpoints to reflect the indentation.
3207 if row == selection.start.row {
3208 selection.start.column += indent_delta.len;
3209 }
3210 if row == selection.end.row {
3211 selection.end.column += indent_delta.len as u32;
3212 }
3213
3214 last_indent = Some((row, indent_delta.len));
3215 }
3216 }
3217 });
3218
3219 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3220 s.select(selections);
3221 });
3222 });
3223 }
3224
3225 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3226 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3227 let selections = self.selections.all::<Point>(cx);
3228 let mut deletion_ranges = Vec::new();
3229 let mut last_outdent = None;
3230 {
3231 let buffer = self.buffer.read(cx);
3232 let snapshot = buffer.snapshot(cx);
3233 for selection in &selections {
3234 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3235 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
3236 let mut rows = selection.spanned_rows(false, &display_map);
3237
3238 // Avoid re-outdenting a row that has already been outdented by a
3239 // previous selection.
3240 if let Some(last_row) = last_outdent {
3241 if last_row == rows.start {
3242 rows.start += 1;
3243 }
3244 }
3245
3246 for row in rows {
3247 let indent_size = snapshot.indent_size_for_line(row);
3248 if indent_size.len > 0 {
3249 let deletion_len = match indent_size.kind {
3250 IndentKind::Space => {
3251 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3252 if columns_to_prev_tab_stop == 0 {
3253 tab_size
3254 } else {
3255 columns_to_prev_tab_stop
3256 }
3257 }
3258 IndentKind::Tab => 1,
3259 };
3260 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3261 last_outdent = Some(row);
3262 }
3263 }
3264 }
3265 }
3266
3267 self.transact(cx, |this, cx| {
3268 this.buffer.update(cx, |buffer, cx| {
3269 let empty_str: Arc<str> = "".into();
3270 buffer.edit(
3271 deletion_ranges
3272 .into_iter()
3273 .map(|range| (range, empty_str.clone())),
3274 cx,
3275 );
3276 });
3277 let selections = this.selections.all::<usize>(cx);
3278 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3279 });
3280 }
3281
3282 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3283 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3284 let selections = self.selections.all::<Point>(cx);
3285
3286 let mut new_cursors = Vec::new();
3287 let mut edit_ranges = Vec::new();
3288 let mut selections = selections.iter().peekable();
3289 while let Some(selection) = selections.next() {
3290 let mut rows = selection.spanned_rows(false, &display_map);
3291 let goal_display_column = selection.head().to_display_point(&display_map).column();
3292
3293 // Accumulate contiguous regions of rows that we want to delete.
3294 while let Some(next_selection) = selections.peek() {
3295 let next_rows = next_selection.spanned_rows(false, &display_map);
3296 if next_rows.start <= rows.end {
3297 rows.end = next_rows.end;
3298 selections.next().unwrap();
3299 } else {
3300 break;
3301 }
3302 }
3303
3304 let buffer = &display_map.buffer_snapshot;
3305 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3306 let edit_end;
3307 let cursor_buffer_row;
3308 if buffer.max_point().row >= rows.end {
3309 // If there's a line after the range, delete the \n from the end of the row range
3310 // and position the cursor on the next line.
3311 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3312 cursor_buffer_row = rows.end;
3313 } else {
3314 // If there isn't a line after the range, delete the \n from the line before the
3315 // start of the row range and position the cursor there.
3316 edit_start = edit_start.saturating_sub(1);
3317 edit_end = buffer.len();
3318 cursor_buffer_row = rows.start.saturating_sub(1);
3319 }
3320
3321 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3322 *cursor.column_mut() =
3323 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3324
3325 new_cursors.push((
3326 selection.id,
3327 buffer.anchor_after(cursor.to_point(&display_map)),
3328 ));
3329 edit_ranges.push(edit_start..edit_end);
3330 }
3331
3332 self.transact(cx, |this, cx| {
3333 let buffer = this.buffer.update(cx, |buffer, cx| {
3334 let empty_str: Arc<str> = "".into();
3335 buffer.edit(
3336 edit_ranges
3337 .into_iter()
3338 .map(|range| (range, empty_str.clone())),
3339 cx,
3340 );
3341 buffer.snapshot(cx)
3342 });
3343 let new_selections = new_cursors
3344 .into_iter()
3345 .map(|(id, cursor)| {
3346 let cursor = cursor.to_point(&buffer);
3347 Selection {
3348 id,
3349 start: cursor,
3350 end: cursor,
3351 reversed: false,
3352 goal: SelectionGoal::None,
3353 }
3354 })
3355 .collect();
3356
3357 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3358 s.select(new_selections);
3359 });
3360 });
3361 }
3362
3363 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3364 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3365 let buffer = &display_map.buffer_snapshot;
3366 let selections = self.selections.all::<Point>(cx);
3367
3368 let mut edits = Vec::new();
3369 let mut selections_iter = selections.iter().peekable();
3370 while let Some(selection) = selections_iter.next() {
3371 // Avoid duplicating the same lines twice.
3372 let mut rows = selection.spanned_rows(false, &display_map);
3373
3374 while let Some(next_selection) = selections_iter.peek() {
3375 let next_rows = next_selection.spanned_rows(false, &display_map);
3376 if next_rows.start <= rows.end - 1 {
3377 rows.end = next_rows.end;
3378 selections_iter.next().unwrap();
3379 } else {
3380 break;
3381 }
3382 }
3383
3384 // Copy the text from the selected row region and splice it at the start of the region.
3385 let start = Point::new(rows.start, 0);
3386 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3387 let text = buffer
3388 .text_for_range(start..end)
3389 .chain(Some("\n"))
3390 .collect::<String>();
3391 edits.push((start..start, text));
3392 }
3393
3394 self.transact(cx, |this, cx| {
3395 this.buffer.update(cx, |buffer, cx| {
3396 buffer.edit(edits, cx);
3397 });
3398
3399 this.request_autoscroll(Autoscroll::Fit, cx);
3400 });
3401 }
3402
3403 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3404 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3405 let buffer = self.buffer.read(cx).snapshot(cx);
3406
3407 let mut edits = Vec::new();
3408 let mut unfold_ranges = Vec::new();
3409 let mut refold_ranges = Vec::new();
3410
3411 let selections = self.selections.all::<Point>(cx);
3412 let mut selections = selections.iter().peekable();
3413 let mut contiguous_row_selections = Vec::new();
3414 let mut new_selections = Vec::new();
3415
3416 while let Some(selection) = selections.next() {
3417 // Find all the selections that span a contiguous row range
3418 contiguous_row_selections.push(selection.clone());
3419 let start_row = selection.start.row;
3420 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3421 display_map.next_line_boundary(selection.end).0.row + 1
3422 } else {
3423 selection.end.row
3424 };
3425
3426 while let Some(next_selection) = selections.peek() {
3427 if next_selection.start.row <= end_row {
3428 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3429 display_map.next_line_boundary(next_selection.end).0.row + 1
3430 } else {
3431 next_selection.end.row
3432 };
3433 contiguous_row_selections.push(selections.next().unwrap().clone());
3434 } else {
3435 break;
3436 }
3437 }
3438
3439 // Move the text spanned by the row range to be before the line preceding the row range
3440 if start_row > 0 {
3441 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3442 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3443 let insertion_point = display_map
3444 .prev_line_boundary(Point::new(start_row - 1, 0))
3445 .0;
3446
3447 // Don't move lines across excerpts
3448 if buffer
3449 .excerpt_boundaries_in_range((
3450 Bound::Excluded(insertion_point),
3451 Bound::Included(range_to_move.end),
3452 ))
3453 .next()
3454 .is_none()
3455 {
3456 let text = buffer
3457 .text_for_range(range_to_move.clone())
3458 .flat_map(|s| s.chars())
3459 .skip(1)
3460 .chain(['\n'])
3461 .collect::<String>();
3462
3463 edits.push((
3464 buffer.anchor_after(range_to_move.start)
3465 ..buffer.anchor_before(range_to_move.end),
3466 String::new(),
3467 ));
3468 let insertion_anchor = buffer.anchor_after(insertion_point);
3469 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3470
3471 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3472
3473 // Move selections up
3474 new_selections.extend(contiguous_row_selections.drain(..).map(
3475 |mut selection| {
3476 selection.start.row -= row_delta;
3477 selection.end.row -= row_delta;
3478 selection
3479 },
3480 ));
3481
3482 // Move folds up
3483 unfold_ranges.push(range_to_move.clone());
3484 for fold in display_map.folds_in_range(
3485 buffer.anchor_before(range_to_move.start)
3486 ..buffer.anchor_after(range_to_move.end),
3487 ) {
3488 let mut start = fold.start.to_point(&buffer);
3489 let mut end = fold.end.to_point(&buffer);
3490 start.row -= row_delta;
3491 end.row -= row_delta;
3492 refold_ranges.push(start..end);
3493 }
3494 }
3495 }
3496
3497 // If we didn't move line(s), preserve the existing selections
3498 new_selections.extend(contiguous_row_selections.drain(..));
3499 }
3500
3501 self.transact(cx, |this, cx| {
3502 this.unfold_ranges(unfold_ranges, true, cx);
3503 this.buffer.update(cx, |buffer, cx| {
3504 for (range, text) in edits {
3505 buffer.edit([(range, text)], cx);
3506 }
3507 });
3508 this.fold_ranges(refold_ranges, cx);
3509 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3510 s.select(new_selections);
3511 })
3512 });
3513 }
3514
3515 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3516 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3517 let buffer = self.buffer.read(cx).snapshot(cx);
3518
3519 let mut edits = Vec::new();
3520 let mut unfold_ranges = Vec::new();
3521 let mut refold_ranges = Vec::new();
3522
3523 let selections = self.selections.all::<Point>(cx);
3524 let mut selections = selections.iter().peekable();
3525 let mut contiguous_row_selections = Vec::new();
3526 let mut new_selections = Vec::new();
3527
3528 while let Some(selection) = selections.next() {
3529 // Find all the selections that span a contiguous row range
3530 contiguous_row_selections.push(selection.clone());
3531 let start_row = selection.start.row;
3532 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3533 display_map.next_line_boundary(selection.end).0.row + 1
3534 } else {
3535 selection.end.row
3536 };
3537
3538 while let Some(next_selection) = selections.peek() {
3539 if next_selection.start.row <= end_row {
3540 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3541 display_map.next_line_boundary(next_selection.end).0.row + 1
3542 } else {
3543 next_selection.end.row
3544 };
3545 contiguous_row_selections.push(selections.next().unwrap().clone());
3546 } else {
3547 break;
3548 }
3549 }
3550
3551 // Move the text spanned by the row range to be after the last line of the row range
3552 if end_row <= buffer.max_point().row {
3553 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3554 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3555
3556 // Don't move lines across excerpt boundaries
3557 if buffer
3558 .excerpt_boundaries_in_range((
3559 Bound::Excluded(range_to_move.start),
3560 Bound::Included(insertion_point),
3561 ))
3562 .next()
3563 .is_none()
3564 {
3565 let mut text = String::from("\n");
3566 text.extend(buffer.text_for_range(range_to_move.clone()));
3567 text.pop(); // Drop trailing newline
3568 edits.push((
3569 buffer.anchor_after(range_to_move.start)
3570 ..buffer.anchor_before(range_to_move.end),
3571 String::new(),
3572 ));
3573 let insertion_anchor = buffer.anchor_after(insertion_point);
3574 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3575
3576 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3577
3578 // Move selections down
3579 new_selections.extend(contiguous_row_selections.drain(..).map(
3580 |mut selection| {
3581 selection.start.row += row_delta;
3582 selection.end.row += row_delta;
3583 selection
3584 },
3585 ));
3586
3587 // Move folds down
3588 unfold_ranges.push(range_to_move.clone());
3589 for fold in display_map.folds_in_range(
3590 buffer.anchor_before(range_to_move.start)
3591 ..buffer.anchor_after(range_to_move.end),
3592 ) {
3593 let mut start = fold.start.to_point(&buffer);
3594 let mut end = fold.end.to_point(&buffer);
3595 start.row += row_delta;
3596 end.row += row_delta;
3597 refold_ranges.push(start..end);
3598 }
3599 }
3600 }
3601
3602 // If we didn't move line(s), preserve the existing selections
3603 new_selections.extend(contiguous_row_selections.drain(..));
3604 }
3605
3606 self.transact(cx, |this, cx| {
3607 this.unfold_ranges(unfold_ranges, true, cx);
3608 this.buffer.update(cx, |buffer, cx| {
3609 for (range, text) in edits {
3610 buffer.edit([(range, text)], cx);
3611 }
3612 });
3613 this.fold_ranges(refold_ranges, cx);
3614 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3615 });
3616 }
3617
3618 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3619 self.transact(cx, |this, cx| {
3620 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3621 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3622 let line_mode = s.line_mode;
3623 s.move_with(|display_map, selection| {
3624 if !selection.is_empty() || line_mode {
3625 return;
3626 }
3627
3628 let mut head = selection.head();
3629 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3630 if head.column() == display_map.line_len(head.row()) {
3631 transpose_offset = display_map
3632 .buffer_snapshot
3633 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3634 }
3635
3636 if transpose_offset == 0 {
3637 return;
3638 }
3639
3640 *head.column_mut() += 1;
3641 head = display_map.clip_point(head, Bias::Right);
3642 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3643
3644 let transpose_start = display_map
3645 .buffer_snapshot
3646 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3647 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3648 let transpose_end = display_map
3649 .buffer_snapshot
3650 .clip_offset(transpose_offset + 1, Bias::Right);
3651 if let Some(ch) =
3652 display_map.buffer_snapshot.chars_at(transpose_start).next()
3653 {
3654 edits.push((transpose_start..transpose_offset, String::new()));
3655 edits.push((transpose_end..transpose_end, ch.to_string()));
3656 }
3657 }
3658 });
3659 edits
3660 });
3661 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3662 let selections = this.selections.all::<usize>(cx);
3663 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3664 s.select(selections);
3665 });
3666 });
3667 }
3668
3669 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3670 let mut text = String::new();
3671 let buffer = self.buffer.read(cx).snapshot(cx);
3672 let mut selections = self.selections.all::<Point>(cx);
3673 let mut clipboard_selections = Vec::with_capacity(selections.len());
3674 {
3675 let max_point = buffer.max_point();
3676 for selection in &mut selections {
3677 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3678 if is_entire_line {
3679 selection.start = Point::new(selection.start.row, 0);
3680 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3681 selection.goal = SelectionGoal::None;
3682 }
3683 let mut len = 0;
3684 for chunk in buffer.text_for_range(selection.start..selection.end) {
3685 text.push_str(chunk);
3686 len += chunk.len();
3687 }
3688 clipboard_selections.push(ClipboardSelection {
3689 len,
3690 is_entire_line,
3691 });
3692 }
3693 }
3694
3695 self.transact(cx, |this, cx| {
3696 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3697 s.select(selections);
3698 });
3699 this.insert("", cx);
3700 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3701 });
3702 }
3703
3704 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3705 let selections = self.selections.all::<Point>(cx);
3706 let buffer = self.buffer.read(cx).read(cx);
3707 let mut text = String::new();
3708
3709 let mut clipboard_selections = Vec::with_capacity(selections.len());
3710 {
3711 let max_point = buffer.max_point();
3712 for selection in selections.iter() {
3713 let mut start = selection.start;
3714 let mut end = selection.end;
3715 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3716 if is_entire_line {
3717 start = Point::new(start.row, 0);
3718 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3719 }
3720 let mut len = 0;
3721 for chunk in buffer.text_for_range(start..end) {
3722 text.push_str(chunk);
3723 len += chunk.len();
3724 }
3725 clipboard_selections.push(ClipboardSelection {
3726 len,
3727 is_entire_line,
3728 });
3729 }
3730 }
3731
3732 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3733 }
3734
3735 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3736 self.transact(cx, |this, cx| {
3737 if let Some(item) = cx.as_mut().read_from_clipboard() {
3738 let mut clipboard_text = Cow::Borrowed(item.text());
3739 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3740 let old_selections = this.selections.all::<usize>(cx);
3741 let all_selections_were_entire_line =
3742 clipboard_selections.iter().all(|s| s.is_entire_line);
3743 if clipboard_selections.len() != old_selections.len() {
3744 let mut newline_separated_text = String::new();
3745 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3746 let mut ix = 0;
3747 while let Some(clipboard_selection) = clipboard_selections.next() {
3748 newline_separated_text
3749 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3750 ix += clipboard_selection.len;
3751 if clipboard_selections.peek().is_some() {
3752 newline_separated_text.push('\n');
3753 }
3754 }
3755 clipboard_text = Cow::Owned(newline_separated_text);
3756 }
3757
3758 this.buffer.update(cx, |buffer, cx| {
3759 let snapshot = buffer.read(cx);
3760 let mut start_offset = 0;
3761 let mut edits = Vec::new();
3762 let line_mode = this.selections.line_mode;
3763 for (ix, selection) in old_selections.iter().enumerate() {
3764 let to_insert;
3765 let entire_line;
3766 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3767 let end_offset = start_offset + clipboard_selection.len;
3768 to_insert = &clipboard_text[start_offset..end_offset];
3769 entire_line = clipboard_selection.is_entire_line;
3770 start_offset = end_offset;
3771 } else {
3772 to_insert = clipboard_text.as_str();
3773 entire_line = all_selections_were_entire_line;
3774 }
3775
3776 // If the corresponding selection was empty when this slice of the
3777 // clipboard text was written, then the entire line containing the
3778 // selection was copied. If this selection is also currently empty,
3779 // then paste the line before the current line of the buffer.
3780 let range = if selection.is_empty() && !line_mode && entire_line {
3781 let column = selection.start.to_point(&snapshot).column as usize;
3782 let line_start = selection.start - column;
3783 line_start..line_start
3784 } else {
3785 selection.range()
3786 };
3787
3788 edits.push((range, to_insert));
3789 }
3790 drop(snapshot);
3791 buffer.edit_with_autoindent(edits, cx);
3792 });
3793
3794 let selections = this.selections.all::<usize>(cx);
3795 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3796 } else {
3797 this.insert(&clipboard_text, cx);
3798 }
3799 }
3800 });
3801 }
3802
3803 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3804 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3805 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3806 self.change_selections(None, cx, |s| {
3807 s.select_anchors(selections.to_vec());
3808 });
3809 }
3810 self.request_autoscroll(Autoscroll::Fit, cx);
3811 cx.emit(Event::Edited);
3812 }
3813 }
3814
3815 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3816 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3817 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3818 {
3819 self.change_selections(None, cx, |s| {
3820 s.select_anchors(selections.to_vec());
3821 });
3822 }
3823 self.request_autoscroll(Autoscroll::Fit, cx);
3824 cx.emit(Event::Edited);
3825 }
3826 }
3827
3828 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3829 self.buffer
3830 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3831 }
3832
3833 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3834 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3835 let line_mode = s.line_mode;
3836 s.move_with(|map, selection| {
3837 let cursor = if selection.is_empty() && !line_mode {
3838 movement::left(map, selection.start)
3839 } else {
3840 selection.start
3841 };
3842 selection.collapse_to(cursor, SelectionGoal::None);
3843 });
3844 })
3845 }
3846
3847 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3848 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3849 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3850 })
3851 }
3852
3853 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3854 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3855 let line_mode = s.line_mode;
3856 s.move_with(|map, selection| {
3857 let cursor = if selection.is_empty() && !line_mode {
3858 movement::right(map, selection.end)
3859 } else {
3860 selection.end
3861 };
3862 selection.collapse_to(cursor, SelectionGoal::None)
3863 });
3864 })
3865 }
3866
3867 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3868 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3869 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3870 })
3871 }
3872
3873 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3874 if self.take_rename(true, cx).is_some() {
3875 return;
3876 }
3877
3878 if let Some(context_menu) = self.context_menu.as_mut() {
3879 if context_menu.select_prev(cx) {
3880 return;
3881 }
3882 }
3883
3884 if matches!(self.mode, EditorMode::SingleLine) {
3885 cx.propagate_action();
3886 return;
3887 }
3888
3889 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3890 let line_mode = s.line_mode;
3891 s.move_with(|map, selection| {
3892 if !selection.is_empty() && !line_mode {
3893 selection.goal = SelectionGoal::None;
3894 }
3895 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3896 selection.collapse_to(cursor, goal);
3897 });
3898 })
3899 }
3900
3901 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3902 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3903 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3904 })
3905 }
3906
3907 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3908 self.take_rename(true, cx);
3909
3910 if let Some(context_menu) = self.context_menu.as_mut() {
3911 if context_menu.select_next(cx) {
3912 return;
3913 }
3914 }
3915
3916 if matches!(self.mode, EditorMode::SingleLine) {
3917 cx.propagate_action();
3918 return;
3919 }
3920
3921 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3922 let line_mode = s.line_mode;
3923 s.move_with(|map, selection| {
3924 if !selection.is_empty() && !line_mode {
3925 selection.goal = SelectionGoal::None;
3926 }
3927 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3928 selection.collapse_to(cursor, goal);
3929 });
3930 });
3931 }
3932
3933 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3934 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3935 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3936 });
3937 }
3938
3939 pub fn move_to_previous_word_start(
3940 &mut self,
3941 _: &MoveToPreviousWordStart,
3942 cx: &mut ViewContext<Self>,
3943 ) {
3944 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3945 s.move_cursors_with(|map, head, _| {
3946 (
3947 movement::previous_word_start(map, head),
3948 SelectionGoal::None,
3949 )
3950 });
3951 })
3952 }
3953
3954 pub fn move_to_previous_subword_start(
3955 &mut self,
3956 _: &MoveToPreviousSubwordStart,
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_subword_start(map, head),
3963 SelectionGoal::None,
3964 )
3965 });
3966 })
3967 }
3968
3969 pub fn select_to_previous_word_start(
3970 &mut self,
3971 _: &SelectToPreviousWordStart,
3972 cx: &mut ViewContext<Self>,
3973 ) {
3974 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3975 s.move_heads_with(|map, head, _| {
3976 (
3977 movement::previous_word_start(map, head),
3978 SelectionGoal::None,
3979 )
3980 });
3981 })
3982 }
3983
3984 pub fn select_to_previous_subword_start(
3985 &mut self,
3986 _: &SelectToPreviousSubwordStart,
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_subword_start(map, head),
3993 SelectionGoal::None,
3994 )
3995 });
3996 })
3997 }
3998
3999 pub fn delete_to_previous_word_start(
4000 &mut self,
4001 _: &DeleteToPreviousWordStart,
4002 cx: &mut ViewContext<Self>,
4003 ) {
4004 self.transact(cx, |this, cx| {
4005 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4006 let line_mode = s.line_mode;
4007 s.move_with(|map, selection| {
4008 if selection.is_empty() && !line_mode {
4009 let cursor = movement::previous_word_start(map, selection.head());
4010 selection.set_head(cursor, SelectionGoal::None);
4011 }
4012 });
4013 });
4014 this.insert("", cx);
4015 });
4016 }
4017
4018 pub fn delete_to_previous_subword_start(
4019 &mut self,
4020 _: &DeleteToPreviousSubwordStart,
4021 cx: &mut ViewContext<Self>,
4022 ) {
4023 self.transact(cx, |this, cx| {
4024 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4025 let line_mode = s.line_mode;
4026 s.move_with(|map, selection| {
4027 if selection.is_empty() && !line_mode {
4028 let cursor = movement::previous_subword_start(map, selection.head());
4029 selection.set_head(cursor, SelectionGoal::None);
4030 }
4031 });
4032 });
4033 this.insert("", cx);
4034 });
4035 }
4036
4037 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4038 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4039 s.move_cursors_with(|map, head, _| {
4040 (movement::next_word_end(map, head), SelectionGoal::None)
4041 });
4042 })
4043 }
4044
4045 pub fn move_to_next_subword_end(
4046 &mut self,
4047 _: &MoveToNextSubwordEnd,
4048 cx: &mut ViewContext<Self>,
4049 ) {
4050 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4051 s.move_cursors_with(|map, head, _| {
4052 (movement::next_subword_end(map, head), SelectionGoal::None)
4053 });
4054 })
4055 }
4056
4057 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4058 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4059 s.move_heads_with(|map, head, _| {
4060 (movement::next_word_end(map, head), SelectionGoal::None)
4061 });
4062 })
4063 }
4064
4065 pub fn select_to_next_subword_end(
4066 &mut self,
4067 _: &SelectToNextSubwordEnd,
4068 cx: &mut ViewContext<Self>,
4069 ) {
4070 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4071 s.move_heads_with(|map, head, _| {
4072 (movement::next_subword_end(map, head), SelectionGoal::None)
4073 });
4074 })
4075 }
4076
4077 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4078 self.transact(cx, |this, cx| {
4079 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4080 let line_mode = s.line_mode;
4081 s.move_with(|map, selection| {
4082 if selection.is_empty() && !line_mode {
4083 let cursor = movement::next_word_end(map, selection.head());
4084 selection.set_head(cursor, SelectionGoal::None);
4085 }
4086 });
4087 });
4088 this.insert("", cx);
4089 });
4090 }
4091
4092 pub fn delete_to_next_subword_end(
4093 &mut self,
4094 _: &DeleteToNextSubwordEnd,
4095 cx: &mut ViewContext<Self>,
4096 ) {
4097 self.transact(cx, |this, cx| {
4098 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4099 s.move_with(|map, selection| {
4100 if selection.is_empty() {
4101 let cursor = movement::next_subword_end(map, selection.head());
4102 selection.set_head(cursor, SelectionGoal::None);
4103 }
4104 });
4105 });
4106 this.insert("", cx);
4107 });
4108 }
4109
4110 pub fn move_to_beginning_of_line(
4111 &mut self,
4112 _: &MoveToBeginningOfLine,
4113 cx: &mut ViewContext<Self>,
4114 ) {
4115 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4116 s.move_cursors_with(|map, head, _| {
4117 (
4118 movement::line_beginning(map, head, true),
4119 SelectionGoal::None,
4120 )
4121 });
4122 })
4123 }
4124
4125 pub fn select_to_beginning_of_line(
4126 &mut self,
4127 action: &SelectToBeginningOfLine,
4128 cx: &mut ViewContext<Self>,
4129 ) {
4130 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4131 s.move_heads_with(|map, head, _| {
4132 (
4133 movement::line_beginning(map, head, action.stop_at_soft_wraps),
4134 SelectionGoal::None,
4135 )
4136 });
4137 });
4138 }
4139
4140 pub fn delete_to_beginning_of_line(
4141 &mut self,
4142 _: &DeleteToBeginningOfLine,
4143 cx: &mut ViewContext<Self>,
4144 ) {
4145 self.transact(cx, |this, cx| {
4146 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4147 s.move_with(|_, selection| {
4148 selection.reversed = true;
4149 });
4150 });
4151
4152 this.select_to_beginning_of_line(
4153 &SelectToBeginningOfLine {
4154 stop_at_soft_wraps: false,
4155 },
4156 cx,
4157 );
4158 this.backspace(&Backspace, cx);
4159 });
4160 }
4161
4162 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4163 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4164 s.move_cursors_with(|map, head, _| {
4165 (movement::line_end(map, head, true), SelectionGoal::None)
4166 });
4167 })
4168 }
4169
4170 pub fn select_to_end_of_line(
4171 &mut self,
4172 action: &SelectToEndOfLine,
4173 cx: &mut ViewContext<Self>,
4174 ) {
4175 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4176 s.move_heads_with(|map, head, _| {
4177 (
4178 movement::line_end(map, head, action.stop_at_soft_wraps),
4179 SelectionGoal::None,
4180 )
4181 });
4182 })
4183 }
4184
4185 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4186 self.transact(cx, |this, cx| {
4187 this.select_to_end_of_line(
4188 &SelectToEndOfLine {
4189 stop_at_soft_wraps: false,
4190 },
4191 cx,
4192 );
4193 this.delete(&Delete, cx);
4194 });
4195 }
4196
4197 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4198 self.transact(cx, |this, cx| {
4199 this.select_to_end_of_line(
4200 &SelectToEndOfLine {
4201 stop_at_soft_wraps: false,
4202 },
4203 cx,
4204 );
4205 this.cut(&Cut, cx);
4206 });
4207 }
4208
4209 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4210 if matches!(self.mode, EditorMode::SingleLine) {
4211 cx.propagate_action();
4212 return;
4213 }
4214
4215 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4216 s.select_ranges(vec![0..0]);
4217 });
4218 }
4219
4220 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4221 let mut selection = self.selections.last::<Point>(cx);
4222 selection.set_head(Point::zero(), SelectionGoal::None);
4223
4224 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4225 s.select(vec![selection]);
4226 });
4227 }
4228
4229 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4230 if matches!(self.mode, EditorMode::SingleLine) {
4231 cx.propagate_action();
4232 return;
4233 }
4234
4235 let cursor = self.buffer.read(cx).read(cx).len();
4236 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4237 s.select_ranges(vec![cursor..cursor])
4238 });
4239 }
4240
4241 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4242 self.nav_history = nav_history;
4243 }
4244
4245 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4246 self.nav_history.as_ref()
4247 }
4248
4249 fn push_to_nav_history(
4250 &self,
4251 position: Anchor,
4252 new_position: Option<Point>,
4253 cx: &mut ViewContext<Self>,
4254 ) {
4255 if let Some(nav_history) = &self.nav_history {
4256 let buffer = self.buffer.read(cx).read(cx);
4257 let point = position.to_point(&buffer);
4258 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4259 drop(buffer);
4260
4261 if let Some(new_position) = new_position {
4262 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4263 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4264 return;
4265 }
4266 }
4267
4268 nav_history.push(Some(NavigationData {
4269 cursor_anchor: position,
4270 cursor_position: point,
4271 scroll_position: self.scroll_position,
4272 scroll_top_anchor: self.scroll_top_anchor.clone(),
4273 scroll_top_row,
4274 }));
4275 }
4276 }
4277
4278 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4279 let buffer = self.buffer.read(cx).snapshot(cx);
4280 let mut selection = self.selections.first::<usize>(cx);
4281 selection.set_head(buffer.len(), SelectionGoal::None);
4282 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4283 s.select(vec![selection]);
4284 });
4285 }
4286
4287 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4288 let end = self.buffer.read(cx).read(cx).len();
4289 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4290 s.select_ranges(vec![0..end]);
4291 });
4292 }
4293
4294 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4295 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4296 let mut selections = self.selections.all::<Point>(cx);
4297 let max_point = display_map.buffer_snapshot.max_point();
4298 for selection in &mut selections {
4299 let rows = selection.spanned_rows(true, &display_map);
4300 selection.start = Point::new(rows.start, 0);
4301 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4302 selection.reversed = false;
4303 }
4304 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4305 s.select(selections);
4306 });
4307 }
4308
4309 pub fn split_selection_into_lines(
4310 &mut self,
4311 _: &SplitSelectionIntoLines,
4312 cx: &mut ViewContext<Self>,
4313 ) {
4314 let mut to_unfold = Vec::new();
4315 let mut new_selection_ranges = Vec::new();
4316 {
4317 let selections = self.selections.all::<Point>(cx);
4318 let buffer = self.buffer.read(cx).read(cx);
4319 for selection in selections {
4320 for row in selection.start.row..selection.end.row {
4321 let cursor = Point::new(row, buffer.line_len(row));
4322 new_selection_ranges.push(cursor..cursor);
4323 }
4324 new_selection_ranges.push(selection.end..selection.end);
4325 to_unfold.push(selection.start..selection.end);
4326 }
4327 }
4328 self.unfold_ranges(to_unfold, true, cx);
4329 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4330 s.select_ranges(new_selection_ranges);
4331 });
4332 }
4333
4334 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4335 self.add_selection(true, cx);
4336 }
4337
4338 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4339 self.add_selection(false, cx);
4340 }
4341
4342 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4343 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4344 let mut selections = self.selections.all::<Point>(cx);
4345 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4346 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4347 let range = oldest_selection.display_range(&display_map).sorted();
4348 let columns = cmp::min(range.start.column(), range.end.column())
4349 ..cmp::max(range.start.column(), range.end.column());
4350
4351 selections.clear();
4352 let mut stack = Vec::new();
4353 for row in range.start.row()..=range.end.row() {
4354 if let Some(selection) = self.selections.build_columnar_selection(
4355 &display_map,
4356 row,
4357 &columns,
4358 oldest_selection.reversed,
4359 ) {
4360 stack.push(selection.id);
4361 selections.push(selection);
4362 }
4363 }
4364
4365 if above {
4366 stack.reverse();
4367 }
4368
4369 AddSelectionsState { above, stack }
4370 });
4371
4372 let last_added_selection = *state.stack.last().unwrap();
4373 let mut new_selections = Vec::new();
4374 if above == state.above {
4375 let end_row = if above {
4376 0
4377 } else {
4378 display_map.max_point().row()
4379 };
4380
4381 'outer: for selection in selections {
4382 if selection.id == last_added_selection {
4383 let range = selection.display_range(&display_map).sorted();
4384 debug_assert_eq!(range.start.row(), range.end.row());
4385 let mut row = range.start.row();
4386 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4387 {
4388 start..end
4389 } else {
4390 cmp::min(range.start.column(), range.end.column())
4391 ..cmp::max(range.start.column(), range.end.column())
4392 };
4393
4394 while row != end_row {
4395 if above {
4396 row -= 1;
4397 } else {
4398 row += 1;
4399 }
4400
4401 if let Some(new_selection) = self.selections.build_columnar_selection(
4402 &display_map,
4403 row,
4404 &columns,
4405 selection.reversed,
4406 ) {
4407 state.stack.push(new_selection.id);
4408 if above {
4409 new_selections.push(new_selection);
4410 new_selections.push(selection);
4411 } else {
4412 new_selections.push(selection);
4413 new_selections.push(new_selection);
4414 }
4415
4416 continue 'outer;
4417 }
4418 }
4419 }
4420
4421 new_selections.push(selection);
4422 }
4423 } else {
4424 new_selections = selections;
4425 new_selections.retain(|s| s.id != last_added_selection);
4426 state.stack.pop();
4427 }
4428
4429 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4430 s.select(new_selections);
4431 });
4432 if state.stack.len() > 1 {
4433 self.add_selections_state = Some(state);
4434 }
4435 }
4436
4437 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4438 self.push_to_selection_history();
4439 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4440 let buffer = &display_map.buffer_snapshot;
4441 let mut selections = self.selections.all::<usize>(cx);
4442 if let Some(mut select_next_state) = self.select_next_state.take() {
4443 let query = &select_next_state.query;
4444 if !select_next_state.done {
4445 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4446 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4447 let mut next_selected_range = None;
4448
4449 let bytes_after_last_selection =
4450 buffer.bytes_in_range(last_selection.end..buffer.len());
4451 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4452 let query_matches = query
4453 .stream_find_iter(bytes_after_last_selection)
4454 .map(|result| (last_selection.end, result))
4455 .chain(
4456 query
4457 .stream_find_iter(bytes_before_first_selection)
4458 .map(|result| (0, result)),
4459 );
4460 for (start_offset, query_match) in query_matches {
4461 let query_match = query_match.unwrap(); // can only fail due to I/O
4462 let offset_range =
4463 start_offset + query_match.start()..start_offset + query_match.end();
4464 let display_range = offset_range.start.to_display_point(&display_map)
4465 ..offset_range.end.to_display_point(&display_map);
4466
4467 if !select_next_state.wordwise
4468 || (!movement::is_inside_word(&display_map, display_range.start)
4469 && !movement::is_inside_word(&display_map, display_range.end))
4470 {
4471 next_selected_range = Some(offset_range);
4472 break;
4473 }
4474 }
4475
4476 if let Some(next_selected_range) = next_selected_range {
4477 self.unfold_ranges([next_selected_range.clone()], false, cx);
4478 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4479 if action.replace_newest {
4480 s.delete(s.newest_anchor().id);
4481 }
4482 s.insert_range(next_selected_range);
4483 });
4484 } else {
4485 select_next_state.done = true;
4486 }
4487 }
4488
4489 self.select_next_state = Some(select_next_state);
4490 } else if selections.len() == 1 {
4491 let selection = selections.last_mut().unwrap();
4492 if selection.start == selection.end {
4493 let word_range = movement::surrounding_word(
4494 &display_map,
4495 selection.start.to_display_point(&display_map),
4496 );
4497 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4498 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4499 selection.goal = SelectionGoal::None;
4500 selection.reversed = false;
4501
4502 let query = buffer
4503 .text_for_range(selection.start..selection.end)
4504 .collect::<String>();
4505 let select_state = SelectNextState {
4506 query: AhoCorasick::new_auto_configured(&[query]),
4507 wordwise: true,
4508 done: false,
4509 };
4510 self.unfold_ranges([selection.start..selection.end], false, cx);
4511 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4512 s.select(selections);
4513 });
4514 self.select_next_state = Some(select_state);
4515 } else {
4516 let query = buffer
4517 .text_for_range(selection.start..selection.end)
4518 .collect::<String>();
4519 self.select_next_state = Some(SelectNextState {
4520 query: AhoCorasick::new_auto_configured(&[query]),
4521 wordwise: false,
4522 done: false,
4523 });
4524 self.select_next(action, cx);
4525 }
4526 }
4527 }
4528
4529 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4530 self.transact(cx, |this, cx| {
4531 let mut selections = this.selections.all::<Point>(cx);
4532 let mut all_selection_lines_are_comments = true;
4533 let mut edit_ranges = Vec::new();
4534 let mut last_toggled_row = None;
4535 this.buffer.update(cx, |buffer, cx| {
4536 // TODO: Handle selections that cross excerpts
4537 for selection in &mut selections {
4538 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4539 // as that portion won't be used for detecting if a line is a comment.
4540 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4541 .language_at(selection.start, cx)
4542 .and_then(|l| l.line_comment_prefix())
4543 {
4544 prefix.into()
4545 } else {
4546 return;
4547 };
4548 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4549 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4550 edit_ranges.clear();
4551 let snapshot = buffer.snapshot(cx);
4552
4553 let end_row =
4554 if selection.end.row > selection.start.row && selection.end.column == 0 {
4555 selection.end.row
4556 } else {
4557 selection.end.row + 1
4558 };
4559
4560 for row in selection.start.row..end_row {
4561 // If multiple selections contain a given row, avoid processing that
4562 // row more than once.
4563 if last_toggled_row == Some(row) {
4564 continue;
4565 } else {
4566 last_toggled_row = Some(row);
4567 }
4568
4569 if snapshot.is_line_blank(row) {
4570 continue;
4571 }
4572
4573 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4574 let mut line_bytes = snapshot
4575 .bytes_in_range(start..snapshot.max_point())
4576 .flatten()
4577 .copied();
4578
4579 // If this line currently begins with the line comment prefix, then record
4580 // the range containing the prefix.
4581 if all_selection_lines_are_comments
4582 && line_bytes
4583 .by_ref()
4584 .take(comment_prefix.len())
4585 .eq(comment_prefix.bytes())
4586 {
4587 // Include any whitespace that matches the comment prefix.
4588 let matching_whitespace_len = line_bytes
4589 .zip(comment_prefix_whitespace.bytes())
4590 .take_while(|(a, b)| a == b)
4591 .count()
4592 as u32;
4593 let end = Point::new(
4594 row,
4595 start.column
4596 + comment_prefix.len() as u32
4597 + matching_whitespace_len,
4598 );
4599 edit_ranges.push(start..end);
4600 }
4601 // If this line does not begin with the line comment prefix, then record
4602 // the position where the prefix should be inserted.
4603 else {
4604 all_selection_lines_are_comments = false;
4605 edit_ranges.push(start..start);
4606 }
4607 }
4608
4609 if !edit_ranges.is_empty() {
4610 if all_selection_lines_are_comments {
4611 let empty_str: Arc<str> = "".into();
4612 buffer.edit(
4613 edit_ranges
4614 .iter()
4615 .cloned()
4616 .map(|range| (range, empty_str.clone())),
4617 cx,
4618 );
4619 } else {
4620 let min_column =
4621 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4622 let edits = edit_ranges.iter().map(|range| {
4623 let position = Point::new(range.start.row, min_column);
4624 (position..position, full_comment_prefix.clone())
4625 });
4626 buffer.edit(edits, cx);
4627 }
4628 }
4629 }
4630 });
4631
4632 let selections = this.selections.all::<usize>(cx);
4633 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4634 });
4635 }
4636
4637 pub fn select_larger_syntax_node(
4638 &mut self,
4639 _: &SelectLargerSyntaxNode,
4640 cx: &mut ViewContext<Self>,
4641 ) {
4642 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4643 let buffer = self.buffer.read(cx).snapshot(cx);
4644 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4645
4646 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4647 let mut selected_larger_node = false;
4648 let new_selections = old_selections
4649 .iter()
4650 .map(|selection| {
4651 let old_range = selection.start..selection.end;
4652 let mut new_range = old_range.clone();
4653 while let Some(containing_range) =
4654 buffer.range_for_syntax_ancestor(new_range.clone())
4655 {
4656 new_range = containing_range;
4657 if !display_map.intersects_fold(new_range.start)
4658 && !display_map.intersects_fold(new_range.end)
4659 {
4660 break;
4661 }
4662 }
4663
4664 selected_larger_node |= new_range != old_range;
4665 Selection {
4666 id: selection.id,
4667 start: new_range.start,
4668 end: new_range.end,
4669 goal: SelectionGoal::None,
4670 reversed: selection.reversed,
4671 }
4672 })
4673 .collect::<Vec<_>>();
4674
4675 if selected_larger_node {
4676 stack.push(old_selections);
4677 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4678 s.select(new_selections);
4679 });
4680 }
4681 self.select_larger_syntax_node_stack = stack;
4682 }
4683
4684 pub fn select_smaller_syntax_node(
4685 &mut self,
4686 _: &SelectSmallerSyntaxNode,
4687 cx: &mut ViewContext<Self>,
4688 ) {
4689 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4690 if let Some(selections) = stack.pop() {
4691 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4692 s.select(selections.to_vec());
4693 });
4694 }
4695 self.select_larger_syntax_node_stack = stack;
4696 }
4697
4698 pub fn move_to_enclosing_bracket(
4699 &mut self,
4700 _: &MoveToEnclosingBracket,
4701 cx: &mut ViewContext<Self>,
4702 ) {
4703 let buffer = self.buffer.read(cx).snapshot(cx);
4704 let mut selections = self.selections.all::<usize>(cx);
4705 for selection in &mut selections {
4706 if let Some((open_range, close_range)) =
4707 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4708 {
4709 let close_range = close_range.to_inclusive();
4710 let destination = if close_range.contains(&selection.start)
4711 && close_range.contains(&selection.end)
4712 {
4713 open_range.end
4714 } else {
4715 *close_range.start()
4716 };
4717 selection.start = destination;
4718 selection.end = destination;
4719 }
4720 }
4721
4722 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4723 s.select(selections);
4724 });
4725 }
4726
4727 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4728 self.end_selection(cx);
4729 self.selection_history.mode = SelectionHistoryMode::Undoing;
4730 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4731 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4732 self.select_next_state = entry.select_next_state;
4733 self.add_selections_state = entry.add_selections_state;
4734 self.request_autoscroll(Autoscroll::Newest, cx);
4735 }
4736 self.selection_history.mode = SelectionHistoryMode::Normal;
4737 }
4738
4739 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4740 self.end_selection(cx);
4741 self.selection_history.mode = SelectionHistoryMode::Redoing;
4742 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4743 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4744 self.select_next_state = entry.select_next_state;
4745 self.add_selections_state = entry.add_selections_state;
4746 self.request_autoscroll(Autoscroll::Newest, cx);
4747 }
4748 self.selection_history.mode = SelectionHistoryMode::Normal;
4749 }
4750
4751 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4752 self.go_to_diagnostic(Direction::Next, cx)
4753 }
4754
4755 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4756 self.go_to_diagnostic(Direction::Prev, cx)
4757 }
4758
4759 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4760 let buffer = self.buffer.read(cx).snapshot(cx);
4761 let selection = self.selections.newest::<usize>(cx);
4762 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4763 active_diagnostics
4764 .primary_range
4765 .to_offset(&buffer)
4766 .to_inclusive()
4767 });
4768 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4769 if active_primary_range.contains(&selection.head()) {
4770 *active_primary_range.end()
4771 } else {
4772 selection.head()
4773 }
4774 } else {
4775 selection.head()
4776 };
4777
4778 loop {
4779 let mut diagnostics = if direction == Direction::Prev {
4780 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4781 } else {
4782 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4783 };
4784 let group = diagnostics.find_map(|entry| {
4785 if entry.diagnostic.is_primary
4786 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4787 && !entry.range.is_empty()
4788 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4789 {
4790 Some((entry.range, entry.diagnostic.group_id))
4791 } else {
4792 None
4793 }
4794 });
4795
4796 if let Some((primary_range, group_id)) = group {
4797 self.activate_diagnostics(group_id, cx);
4798 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4799 s.select(vec![Selection {
4800 id: selection.id,
4801 start: primary_range.start,
4802 end: primary_range.start,
4803 reversed: false,
4804 goal: SelectionGoal::None,
4805 }]);
4806 });
4807 break;
4808 } else {
4809 // Cycle around to the start of the buffer, potentially moving back to the start of
4810 // the currently active diagnostic.
4811 active_primary_range.take();
4812 if direction == Direction::Prev {
4813 if search_start == buffer.len() {
4814 break;
4815 } else {
4816 search_start = buffer.len();
4817 }
4818 } else {
4819 if search_start == 0 {
4820 break;
4821 } else {
4822 search_start = 0;
4823 }
4824 }
4825 }
4826 }
4827 }
4828
4829 pub fn go_to_definition(
4830 workspace: &mut Workspace,
4831 _: &GoToDefinition,
4832 cx: &mut ViewContext<Workspace>,
4833 ) {
4834 let active_item = workspace.active_item(cx);
4835 let editor_handle = if let Some(editor) = active_item
4836 .as_ref()
4837 .and_then(|item| item.act_as::<Self>(cx))
4838 {
4839 editor
4840 } else {
4841 return;
4842 };
4843
4844 let editor = editor_handle.read(cx);
4845 let buffer = editor.buffer.read(cx);
4846 let head = editor.selections.newest::<usize>(cx).head();
4847 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4848 text_anchor
4849 } else {
4850 return;
4851 };
4852
4853 let project = workspace.project().clone();
4854 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4855 cx.spawn(|workspace, mut cx| async move {
4856 let definitions = definitions.await?;
4857 workspace.update(&mut cx, |workspace, cx| {
4858 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4859 for definition in definitions {
4860 let range = definition.range.to_offset(definition.buffer.read(cx));
4861
4862 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4863 target_editor_handle.update(cx, |target_editor, cx| {
4864 // When selecting a definition in a different buffer, disable the nav history
4865 // to avoid creating a history entry at the previous cursor location.
4866 if editor_handle != target_editor_handle {
4867 nav_history.borrow_mut().disable();
4868 }
4869 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4870 s.select_ranges([range]);
4871 });
4872
4873 nav_history.borrow_mut().enable();
4874 });
4875 }
4876 });
4877
4878 Ok::<(), anyhow::Error>(())
4879 })
4880 .detach_and_log_err(cx);
4881 }
4882
4883 pub fn find_all_references(
4884 workspace: &mut Workspace,
4885 _: &FindAllReferences,
4886 cx: &mut ViewContext<Workspace>,
4887 ) -> Option<Task<Result<()>>> {
4888 let active_item = workspace.active_item(cx)?;
4889 let editor_handle = active_item.act_as::<Self>(cx)?;
4890
4891 let editor = editor_handle.read(cx);
4892 let buffer = editor.buffer.read(cx);
4893 let head = editor.selections.newest::<usize>(cx).head();
4894 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4895 let replica_id = editor.replica_id(cx);
4896
4897 let project = workspace.project().clone();
4898 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4899 Some(cx.spawn(|workspace, mut cx| async move {
4900 let mut locations = references.await?;
4901 if locations.is_empty() {
4902 return Ok(());
4903 }
4904
4905 locations.sort_by_key(|location| location.buffer.id());
4906 let mut locations = locations.into_iter().peekable();
4907 let mut ranges_to_highlight = Vec::new();
4908
4909 let excerpt_buffer = cx.add_model(|cx| {
4910 let mut symbol_name = None;
4911 let mut multibuffer = MultiBuffer::new(replica_id);
4912 while let Some(location) = locations.next() {
4913 let buffer = location.buffer.read(cx);
4914 let mut ranges_for_buffer = Vec::new();
4915 let range = location.range.to_offset(buffer);
4916 ranges_for_buffer.push(range.clone());
4917 if symbol_name.is_none() {
4918 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4919 }
4920
4921 while let Some(next_location) = locations.peek() {
4922 if next_location.buffer == location.buffer {
4923 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4924 locations.next();
4925 } else {
4926 break;
4927 }
4928 }
4929
4930 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4931 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4932 location.buffer.clone(),
4933 ranges_for_buffer,
4934 1,
4935 cx,
4936 ));
4937 }
4938 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4939 });
4940
4941 workspace.update(&mut cx, |workspace, cx| {
4942 let editor =
4943 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4944 editor.update(cx, |editor, cx| {
4945 editor.highlight_background::<Self>(
4946 ranges_to_highlight,
4947 |theme| theme.editor.highlighted_line_background,
4948 cx,
4949 );
4950 });
4951 workspace.add_item(Box::new(editor), cx);
4952 });
4953
4954 Ok(())
4955 }))
4956 }
4957
4958 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4959 use language::ToOffset as _;
4960
4961 let project = self.project.clone()?;
4962 let selection = self.selections.newest_anchor().clone();
4963 let (cursor_buffer, cursor_buffer_position) = self
4964 .buffer
4965 .read(cx)
4966 .text_anchor_for_position(selection.head(), cx)?;
4967 let (tail_buffer, _) = self
4968 .buffer
4969 .read(cx)
4970 .text_anchor_for_position(selection.tail(), cx)?;
4971 if tail_buffer != cursor_buffer {
4972 return None;
4973 }
4974
4975 let snapshot = cursor_buffer.read(cx).snapshot();
4976 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4977 let prepare_rename = project.update(cx, |project, cx| {
4978 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4979 });
4980
4981 Some(cx.spawn(|this, mut cx| async move {
4982 let rename_range = if let Some(range) = prepare_rename.await? {
4983 Some(range)
4984 } else {
4985 this.read_with(&cx, |this, cx| {
4986 let buffer = this.buffer.read(cx).snapshot(cx);
4987 let mut buffer_highlights = this
4988 .document_highlights_for_position(selection.head(), &buffer)
4989 .filter(|highlight| {
4990 highlight.start.excerpt_id() == selection.head().excerpt_id()
4991 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4992 });
4993 buffer_highlights
4994 .next()
4995 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4996 })
4997 };
4998 if let Some(rename_range) = rename_range {
4999 let rename_buffer_range = rename_range.to_offset(&snapshot);
5000 let cursor_offset_in_rename_range =
5001 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5002
5003 this.update(&mut cx, |this, cx| {
5004 this.take_rename(false, cx);
5005 let style = this.style(cx);
5006 let buffer = this.buffer.read(cx).read(cx);
5007 let cursor_offset = selection.head().to_offset(&buffer);
5008 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5009 let rename_end = rename_start + rename_buffer_range.len();
5010 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5011 let mut old_highlight_id = None;
5012 let old_name: Arc<str> = buffer
5013 .chunks(rename_start..rename_end, true)
5014 .map(|chunk| {
5015 if old_highlight_id.is_none() {
5016 old_highlight_id = chunk.syntax_highlight_id;
5017 }
5018 chunk.text
5019 })
5020 .collect::<String>()
5021 .into();
5022
5023 drop(buffer);
5024
5025 // Position the selection in the rename editor so that it matches the current selection.
5026 this.show_local_selections = false;
5027 let rename_editor = cx.add_view(|cx| {
5028 let mut editor = Editor::single_line(None, cx);
5029 if let Some(old_highlight_id) = old_highlight_id {
5030 editor.override_text_style =
5031 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5032 }
5033 editor
5034 .buffer
5035 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
5036 editor.select_all(&SelectAll, cx);
5037 editor
5038 });
5039
5040 let ranges = this
5041 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5042 .into_iter()
5043 .flat_map(|(_, ranges)| ranges)
5044 .chain(
5045 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5046 .into_iter()
5047 .flat_map(|(_, ranges)| ranges),
5048 )
5049 .collect();
5050
5051 this.highlight_text::<Rename>(
5052 ranges,
5053 HighlightStyle {
5054 fade_out: Some(style.rename_fade),
5055 ..Default::default()
5056 },
5057 cx,
5058 );
5059 cx.focus(&rename_editor);
5060 let block_id = this.insert_blocks(
5061 [BlockProperties {
5062 position: range.start.clone(),
5063 height: 1,
5064 render: Arc::new({
5065 let editor = rename_editor.clone();
5066 move |cx: &mut BlockContext| {
5067 ChildView::new(editor.clone())
5068 .contained()
5069 .with_padding_left(cx.anchor_x)
5070 .boxed()
5071 }
5072 }),
5073 disposition: BlockDisposition::Below,
5074 }],
5075 cx,
5076 )[0];
5077 this.pending_rename = Some(RenameState {
5078 range,
5079 old_name,
5080 editor: rename_editor,
5081 block_id,
5082 });
5083 });
5084 }
5085
5086 Ok(())
5087 }))
5088 }
5089
5090 pub fn confirm_rename(
5091 workspace: &mut Workspace,
5092 _: &ConfirmRename,
5093 cx: &mut ViewContext<Workspace>,
5094 ) -> Option<Task<Result<()>>> {
5095 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5096
5097 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5098 let rename = editor.take_rename(false, cx)?;
5099 let buffer = editor.buffer.read(cx);
5100 let (start_buffer, start) =
5101 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5102 let (end_buffer, end) =
5103 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5104 if start_buffer == end_buffer {
5105 let new_name = rename.editor.read(cx).text(cx);
5106 Some((start_buffer, start..end, rename.old_name, new_name))
5107 } else {
5108 None
5109 }
5110 })?;
5111
5112 let rename = workspace.project().clone().update(cx, |project, cx| {
5113 project.perform_rename(
5114 buffer.clone(),
5115 range.start.clone(),
5116 new_name.clone(),
5117 true,
5118 cx,
5119 )
5120 });
5121
5122 Some(cx.spawn(|workspace, mut cx| async move {
5123 let project_transaction = rename.await?;
5124 Self::open_project_transaction(
5125 editor.clone(),
5126 workspace,
5127 project_transaction,
5128 format!("Rename: {} → {}", old_name, new_name),
5129 cx.clone(),
5130 )
5131 .await?;
5132
5133 editor.update(&mut cx, |editor, cx| {
5134 editor.refresh_document_highlights(cx);
5135 });
5136 Ok(())
5137 }))
5138 }
5139
5140 fn take_rename(
5141 &mut self,
5142 moving_cursor: bool,
5143 cx: &mut ViewContext<Self>,
5144 ) -> Option<RenameState> {
5145 let rename = self.pending_rename.take()?;
5146 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5147 self.clear_text_highlights::<Rename>(cx);
5148 self.show_local_selections = true;
5149
5150 if moving_cursor {
5151 let rename_editor = rename.editor.read(cx);
5152 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5153
5154 // Update the selection to match the position of the selection inside
5155 // the rename editor.
5156 let snapshot = self.buffer.read(cx).read(cx);
5157 let rename_range = rename.range.to_offset(&snapshot);
5158 let cursor_in_editor = snapshot
5159 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5160 .min(rename_range.end);
5161 drop(snapshot);
5162
5163 self.change_selections(None, cx, |s| {
5164 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5165 });
5166 }
5167
5168 Some(rename)
5169 }
5170
5171 #[cfg(any(test, feature = "test-support"))]
5172 pub fn pending_rename(&self) -> Option<&RenameState> {
5173 self.pending_rename.as_ref()
5174 }
5175
5176 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5177 if let Some(project) = self.project.clone() {
5178 self.buffer.update(cx, |multi_buffer, cx| {
5179 project.update(cx, |project, cx| {
5180 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5181 });
5182 })
5183 }
5184 }
5185
5186 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5187 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5188 let buffer = self.buffer.read(cx).snapshot(cx);
5189 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5190 let is_valid = buffer
5191 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5192 .any(|entry| {
5193 entry.diagnostic.is_primary
5194 && !entry.range.is_empty()
5195 && entry.range.start == primary_range_start
5196 && entry.diagnostic.message == active_diagnostics.primary_message
5197 });
5198
5199 if is_valid != active_diagnostics.is_valid {
5200 active_diagnostics.is_valid = is_valid;
5201 let mut new_styles = HashMap::default();
5202 for (block_id, diagnostic) in &active_diagnostics.blocks {
5203 new_styles.insert(
5204 *block_id,
5205 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5206 );
5207 }
5208 self.display_map
5209 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5210 }
5211 }
5212 }
5213
5214 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5215 self.dismiss_diagnostics(cx);
5216 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5217 let buffer = self.buffer.read(cx).snapshot(cx);
5218
5219 let mut primary_range = None;
5220 let mut primary_message = None;
5221 let mut group_end = Point::zero();
5222 let diagnostic_group = buffer
5223 .diagnostic_group::<Point>(group_id)
5224 .map(|entry| {
5225 if entry.range.end > group_end {
5226 group_end = entry.range.end;
5227 }
5228 if entry.diagnostic.is_primary {
5229 primary_range = Some(entry.range.clone());
5230 primary_message = Some(entry.diagnostic.message.clone());
5231 }
5232 entry
5233 })
5234 .collect::<Vec<_>>();
5235 let primary_range = primary_range.unwrap();
5236 let primary_message = primary_message.unwrap();
5237 let primary_range =
5238 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5239
5240 let blocks = display_map
5241 .insert_blocks(
5242 diagnostic_group.iter().map(|entry| {
5243 let diagnostic = entry.diagnostic.clone();
5244 let message_height = diagnostic.message.lines().count() as u8;
5245 BlockProperties {
5246 position: buffer.anchor_after(entry.range.start),
5247 height: message_height,
5248 render: diagnostic_block_renderer(diagnostic, true),
5249 disposition: BlockDisposition::Below,
5250 }
5251 }),
5252 cx,
5253 )
5254 .into_iter()
5255 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5256 .collect();
5257
5258 Some(ActiveDiagnosticGroup {
5259 primary_range,
5260 primary_message,
5261 blocks,
5262 is_valid: true,
5263 })
5264 });
5265 }
5266
5267 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5268 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5269 self.display_map.update(cx, |display_map, cx| {
5270 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5271 });
5272 cx.notify();
5273 }
5274 }
5275
5276 pub fn set_selections_from_remote(
5277 &mut self,
5278 selections: Vec<Selection<Anchor>>,
5279 cx: &mut ViewContext<Self>,
5280 ) {
5281 let old_cursor_position = self.selections.newest_anchor().head();
5282 self.selections.change_with(cx, |s| {
5283 s.select_anchors(selections);
5284 });
5285 self.selections_did_change(false, &old_cursor_position, cx);
5286 }
5287
5288 fn push_to_selection_history(&mut self) {
5289 self.selection_history.push(SelectionHistoryEntry {
5290 selections: self.selections.disjoint_anchors().clone(),
5291 select_next_state: self.select_next_state.clone(),
5292 add_selections_state: self.add_selections_state.clone(),
5293 });
5294 }
5295
5296 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5297 self.autoscroll_request = Some((autoscroll, true));
5298 cx.notify();
5299 }
5300
5301 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5302 self.autoscroll_request = Some((autoscroll, false));
5303 cx.notify();
5304 }
5305
5306 pub fn transact(
5307 &mut self,
5308 cx: &mut ViewContext<Self>,
5309 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5310 ) {
5311 self.start_transaction_at(Instant::now(), cx);
5312 update(self, cx);
5313 self.end_transaction_at(Instant::now(), cx);
5314 }
5315
5316 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5317 self.end_selection(cx);
5318 if let Some(tx_id) = self
5319 .buffer
5320 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5321 {
5322 self.selection_history
5323 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5324 }
5325 }
5326
5327 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5328 if let Some(tx_id) = self
5329 .buffer
5330 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5331 {
5332 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5333 *end_selections = Some(self.selections.disjoint_anchors().clone());
5334 } else {
5335 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5336 }
5337
5338 cx.emit(Event::Edited);
5339 }
5340 }
5341
5342 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5343 log::info!("Editor::page_up");
5344 }
5345
5346 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5347 log::info!("Editor::page_down");
5348 }
5349
5350 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5351 let mut fold_ranges = Vec::new();
5352
5353 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5354 let selections = self.selections.all::<Point>(cx);
5355 for selection in selections {
5356 let range = selection.display_range(&display_map).sorted();
5357 let buffer_start_row = range.start.to_point(&display_map).row;
5358
5359 for row in (0..=range.end.row()).rev() {
5360 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5361 let fold_range = self.foldable_range_for_line(&display_map, row);
5362 if fold_range.end.row >= buffer_start_row {
5363 fold_ranges.push(fold_range);
5364 if row <= range.start.row() {
5365 break;
5366 }
5367 }
5368 }
5369 }
5370 }
5371
5372 self.fold_ranges(fold_ranges, cx);
5373 }
5374
5375 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5376 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5377 let buffer = &display_map.buffer_snapshot;
5378 let selections = self.selections.all::<Point>(cx);
5379 let ranges = selections
5380 .iter()
5381 .map(|s| {
5382 let range = s.display_range(&display_map).sorted();
5383 let mut start = range.start.to_point(&display_map);
5384 let mut end = range.end.to_point(&display_map);
5385 start.column = 0;
5386 end.column = buffer.line_len(end.row);
5387 start..end
5388 })
5389 .collect::<Vec<_>>();
5390 self.unfold_ranges(ranges, true, cx);
5391 }
5392
5393 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5394 let max_point = display_map.max_point();
5395 if display_row >= max_point.row() {
5396 false
5397 } else {
5398 let (start_indent, is_blank) = display_map.line_indent(display_row);
5399 if is_blank {
5400 false
5401 } else {
5402 for display_row in display_row + 1..=max_point.row() {
5403 let (indent, is_blank) = display_map.line_indent(display_row);
5404 if !is_blank {
5405 return indent > start_indent;
5406 }
5407 }
5408 false
5409 }
5410 }
5411 }
5412
5413 fn foldable_range_for_line(
5414 &self,
5415 display_map: &DisplaySnapshot,
5416 start_row: u32,
5417 ) -> Range<Point> {
5418 let max_point = display_map.max_point();
5419
5420 let (start_indent, _) = display_map.line_indent(start_row);
5421 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5422 let mut end = None;
5423 for row in start_row + 1..=max_point.row() {
5424 let (indent, is_blank) = display_map.line_indent(row);
5425 if !is_blank && indent <= start_indent {
5426 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5427 break;
5428 }
5429 }
5430
5431 let end = end.unwrap_or(max_point);
5432 return start.to_point(display_map)..end.to_point(display_map);
5433 }
5434
5435 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5436 let selections = self.selections.all::<Point>(cx);
5437 let ranges = selections.into_iter().map(|s| s.start..s.end);
5438 self.fold_ranges(ranges, cx);
5439 }
5440
5441 pub fn fold_ranges<T: ToOffset>(
5442 &mut self,
5443 ranges: impl IntoIterator<Item = Range<T>>,
5444 cx: &mut ViewContext<Self>,
5445 ) {
5446 let mut ranges = ranges.into_iter().peekable();
5447 if ranges.peek().is_some() {
5448 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5449 self.request_autoscroll(Autoscroll::Fit, cx);
5450 cx.notify();
5451 }
5452 }
5453
5454 pub fn unfold_ranges<T: ToOffset>(
5455 &mut self,
5456 ranges: impl IntoIterator<Item = Range<T>>,
5457 inclusive: bool,
5458 cx: &mut ViewContext<Self>,
5459 ) {
5460 let mut ranges = ranges.into_iter().peekable();
5461 if ranges.peek().is_some() {
5462 self.display_map
5463 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5464 self.request_autoscroll(Autoscroll::Fit, cx);
5465 cx.notify();
5466 }
5467 }
5468
5469 pub fn insert_blocks(
5470 &mut self,
5471 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5472 cx: &mut ViewContext<Self>,
5473 ) -> Vec<BlockId> {
5474 let blocks = self
5475 .display_map
5476 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5477 self.request_autoscroll(Autoscroll::Fit, cx);
5478 blocks
5479 }
5480
5481 pub fn replace_blocks(
5482 &mut self,
5483 blocks: HashMap<BlockId, RenderBlock>,
5484 cx: &mut ViewContext<Self>,
5485 ) {
5486 self.display_map
5487 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5488 self.request_autoscroll(Autoscroll::Fit, cx);
5489 }
5490
5491 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5492 self.display_map.update(cx, |display_map, cx| {
5493 display_map.remove_blocks(block_ids, cx)
5494 });
5495 }
5496
5497 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5498 self.display_map
5499 .update(cx, |map, cx| map.snapshot(cx))
5500 .longest_row()
5501 }
5502
5503 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5504 self.display_map
5505 .update(cx, |map, cx| map.snapshot(cx))
5506 .max_point()
5507 }
5508
5509 pub fn text(&self, cx: &AppContext) -> String {
5510 self.buffer.read(cx).read(cx).text()
5511 }
5512
5513 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5514 self.transact(cx, |this, cx| {
5515 this.buffer
5516 .read(cx)
5517 .as_singleton()
5518 .expect("you can only call set_text on editors for singleton buffers")
5519 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5520 });
5521 }
5522
5523 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5524 self.display_map
5525 .update(cx, |map, cx| map.snapshot(cx))
5526 .text()
5527 }
5528
5529 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5530 let language_name = self
5531 .buffer
5532 .read(cx)
5533 .as_singleton()
5534 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5535 .map(|l| l.name());
5536
5537 let settings = cx.global::<Settings>();
5538 let mode = self
5539 .soft_wrap_mode_override
5540 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5541 match mode {
5542 settings::SoftWrap::None => SoftWrap::None,
5543 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5544 settings::SoftWrap::PreferredLineLength => {
5545 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5546 }
5547 }
5548 }
5549
5550 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5551 self.soft_wrap_mode_override = Some(mode);
5552 cx.notify();
5553 }
5554
5555 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5556 self.display_map
5557 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5558 }
5559
5560 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5561 self.highlighted_rows = rows;
5562 }
5563
5564 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5565 self.highlighted_rows.clone()
5566 }
5567
5568 pub fn highlight_background<T: 'static>(
5569 &mut self,
5570 ranges: Vec<Range<Anchor>>,
5571 color_fetcher: fn(&Theme) -> Color,
5572 cx: &mut ViewContext<Self>,
5573 ) {
5574 self.background_highlights
5575 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5576 cx.notify();
5577 }
5578
5579 pub fn clear_background_highlights<T: 'static>(
5580 &mut self,
5581 cx: &mut ViewContext<Self>,
5582 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5583 cx.notify();
5584 self.background_highlights.remove(&TypeId::of::<T>())
5585 }
5586
5587 #[cfg(feature = "test-support")]
5588 pub fn all_background_highlights(
5589 &mut self,
5590 cx: &mut ViewContext<Self>,
5591 ) -> Vec<(Range<DisplayPoint>, Color)> {
5592 let snapshot = self.snapshot(cx);
5593 let buffer = &snapshot.buffer_snapshot;
5594 let start = buffer.anchor_before(0);
5595 let end = buffer.anchor_after(buffer.len());
5596 let theme = cx.global::<Settings>().theme.as_ref();
5597 self.background_highlights_in_range(start..end, &snapshot, theme)
5598 }
5599
5600 fn document_highlights_for_position<'a>(
5601 &'a self,
5602 position: Anchor,
5603 buffer: &'a MultiBufferSnapshot,
5604 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5605 let read_highlights = self
5606 .background_highlights
5607 .get(&TypeId::of::<DocumentHighlightRead>())
5608 .map(|h| &h.1);
5609 let write_highlights = self
5610 .background_highlights
5611 .get(&TypeId::of::<DocumentHighlightRead>())
5612 .map(|h| &h.1);
5613 let left_position = position.bias_left(buffer);
5614 let right_position = position.bias_right(buffer);
5615 read_highlights
5616 .into_iter()
5617 .chain(write_highlights)
5618 .flat_map(move |ranges| {
5619 let start_ix = match ranges.binary_search_by(|probe| {
5620 let cmp = probe.end.cmp(&left_position, &buffer);
5621 if cmp.is_ge() {
5622 Ordering::Greater
5623 } else {
5624 Ordering::Less
5625 }
5626 }) {
5627 Ok(i) | Err(i) => i,
5628 };
5629
5630 let right_position = right_position.clone();
5631 ranges[start_ix..]
5632 .iter()
5633 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5634 })
5635 }
5636
5637 pub fn background_highlights_in_range(
5638 &self,
5639 search_range: Range<Anchor>,
5640 display_snapshot: &DisplaySnapshot,
5641 theme: &Theme,
5642 ) -> Vec<(Range<DisplayPoint>, Color)> {
5643 let mut results = Vec::new();
5644 let buffer = &display_snapshot.buffer_snapshot;
5645 for (color_fetcher, ranges) in self.background_highlights.values() {
5646 let color = color_fetcher(theme);
5647 let start_ix = match ranges.binary_search_by(|probe| {
5648 let cmp = probe.end.cmp(&search_range.start, &buffer);
5649 if cmp.is_gt() {
5650 Ordering::Greater
5651 } else {
5652 Ordering::Less
5653 }
5654 }) {
5655 Ok(i) | Err(i) => i,
5656 };
5657 for range in &ranges[start_ix..] {
5658 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5659 break;
5660 }
5661 let start = range
5662 .start
5663 .to_point(buffer)
5664 .to_display_point(display_snapshot);
5665 let end = range
5666 .end
5667 .to_point(buffer)
5668 .to_display_point(display_snapshot);
5669 results.push((start..end, color))
5670 }
5671 }
5672 results
5673 }
5674
5675 pub fn highlight_text<T: 'static>(
5676 &mut self,
5677 ranges: Vec<Range<Anchor>>,
5678 style: HighlightStyle,
5679 cx: &mut ViewContext<Self>,
5680 ) {
5681 self.display_map.update(cx, |map, _| {
5682 map.highlight_text(TypeId::of::<T>(), ranges, style)
5683 });
5684 cx.notify();
5685 }
5686
5687 pub fn clear_text_highlights<T: 'static>(
5688 &mut self,
5689 cx: &mut ViewContext<Self>,
5690 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5691 cx.notify();
5692 self.display_map
5693 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5694 }
5695
5696 fn next_blink_epoch(&mut self) -> usize {
5697 self.blink_epoch += 1;
5698 self.blink_epoch
5699 }
5700
5701 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5702 if !self.focused {
5703 return;
5704 }
5705
5706 self.show_local_cursors = true;
5707 cx.notify();
5708
5709 let epoch = self.next_blink_epoch();
5710 cx.spawn(|this, mut cx| {
5711 let this = this.downgrade();
5712 async move {
5713 Timer::after(CURSOR_BLINK_INTERVAL).await;
5714 if let Some(this) = this.upgrade(&cx) {
5715 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5716 }
5717 }
5718 })
5719 .detach();
5720 }
5721
5722 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5723 if epoch == self.blink_epoch {
5724 self.blinking_paused = false;
5725 self.blink_cursors(epoch, cx);
5726 }
5727 }
5728
5729 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5730 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5731 self.show_local_cursors = !self.show_local_cursors;
5732 cx.notify();
5733
5734 let epoch = self.next_blink_epoch();
5735 cx.spawn(|this, mut cx| {
5736 let this = this.downgrade();
5737 async move {
5738 Timer::after(CURSOR_BLINK_INTERVAL).await;
5739 if let Some(this) = this.upgrade(&cx) {
5740 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5741 }
5742 }
5743 })
5744 .detach();
5745 }
5746 }
5747
5748 pub fn show_local_cursors(&self) -> bool {
5749 self.show_local_cursors && self.focused
5750 }
5751
5752 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5753 cx.notify();
5754 }
5755
5756 fn on_buffer_event(
5757 &mut self,
5758 _: ModelHandle<MultiBuffer>,
5759 event: &language::Event,
5760 cx: &mut ViewContext<Self>,
5761 ) {
5762 match event {
5763 language::Event::Edited => {
5764 self.refresh_active_diagnostics(cx);
5765 self.refresh_code_actions(cx);
5766 cx.emit(Event::BufferEdited);
5767 }
5768 language::Event::Reparsed => cx.emit(Event::Reparsed),
5769 language::Event::Dirtied => cx.emit(Event::Dirtied),
5770 language::Event::Saved => cx.emit(Event::Saved),
5771 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5772 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5773 language::Event::Closed => cx.emit(Event::Closed),
5774 language::Event::DiagnosticsUpdated => {
5775 self.refresh_active_diagnostics(cx);
5776 }
5777 _ => {}
5778 }
5779 }
5780
5781 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5782 cx.notify();
5783 }
5784
5785 pub fn set_searchable(&mut self, searchable: bool) {
5786 self.searchable = searchable;
5787 }
5788
5789 pub fn searchable(&self) -> bool {
5790 self.searchable
5791 }
5792
5793 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5794 let active_item = workspace.active_item(cx);
5795 let editor_handle = if let Some(editor) = active_item
5796 .as_ref()
5797 .and_then(|item| item.act_as::<Self>(cx))
5798 {
5799 editor
5800 } else {
5801 cx.propagate_action();
5802 return;
5803 };
5804
5805 let editor = editor_handle.read(cx);
5806 let buffer = editor.buffer.read(cx);
5807 if buffer.is_singleton() {
5808 cx.propagate_action();
5809 return;
5810 }
5811
5812 let mut new_selections_by_buffer = HashMap::default();
5813 for selection in editor.selections.all::<usize>(cx) {
5814 for (buffer, mut range) in
5815 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5816 {
5817 if selection.reversed {
5818 mem::swap(&mut range.start, &mut range.end);
5819 }
5820 new_selections_by_buffer
5821 .entry(buffer)
5822 .or_insert(Vec::new())
5823 .push(range)
5824 }
5825 }
5826
5827 editor_handle.update(cx, |editor, cx| {
5828 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5829 });
5830 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5831 nav_history.borrow_mut().disable();
5832
5833 // We defer the pane interaction because we ourselves are a workspace item
5834 // and activating a new item causes the pane to call a method on us reentrantly,
5835 // which panics if we're on the stack.
5836 cx.defer(move |workspace, cx| {
5837 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5838 let editor = workspace.open_project_item::<Self>(buffer, cx);
5839 editor.update(cx, |editor, cx| {
5840 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5841 s.select_ranges(ranges);
5842 });
5843 });
5844 }
5845
5846 nav_history.borrow_mut().enable();
5847 });
5848 }
5849}
5850
5851impl EditorSnapshot {
5852 pub fn is_focused(&self) -> bool {
5853 self.is_focused
5854 }
5855
5856 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5857 self.placeholder_text.as_ref()
5858 }
5859
5860 pub fn scroll_position(&self) -> Vector2F {
5861 compute_scroll_position(
5862 &self.display_snapshot,
5863 self.scroll_position,
5864 &self.scroll_top_anchor,
5865 )
5866 }
5867}
5868
5869impl Deref for EditorSnapshot {
5870 type Target = DisplaySnapshot;
5871
5872 fn deref(&self) -> &Self::Target {
5873 &self.display_snapshot
5874 }
5875}
5876
5877fn compute_scroll_position(
5878 snapshot: &DisplaySnapshot,
5879 mut scroll_position: Vector2F,
5880 scroll_top_anchor: &Anchor,
5881) -> Vector2F {
5882 if *scroll_top_anchor != Anchor::min() {
5883 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5884 scroll_position.set_y(scroll_top + scroll_position.y());
5885 } else {
5886 scroll_position.set_y(0.);
5887 }
5888 scroll_position
5889}
5890
5891#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5892pub enum Event {
5893 Activate,
5894 BufferEdited,
5895 Edited,
5896 Reparsed,
5897 Blurred,
5898 Dirtied,
5899 Saved,
5900 TitleChanged,
5901 SelectionsChanged { local: bool },
5902 ScrollPositionChanged { local: bool },
5903 Closed,
5904}
5905
5906pub struct EditorFocused(pub ViewHandle<Editor>);
5907pub struct EditorBlurred(pub ViewHandle<Editor>);
5908pub struct EditorReleased(pub WeakViewHandle<Editor>);
5909
5910impl Entity for Editor {
5911 type Event = Event;
5912
5913 fn release(&mut self, cx: &mut MutableAppContext) {
5914 cx.emit_global(EditorReleased(self.handle.clone()));
5915 }
5916}
5917
5918impl View for Editor {
5919 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5920 let style = self.style(cx);
5921 self.display_map.update(cx, |map, cx| {
5922 map.set_font(style.text.font_id, style.text.font_size, cx)
5923 });
5924 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5925 }
5926
5927 fn ui_name() -> &'static str {
5928 "Editor"
5929 }
5930
5931 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5932 let focused_event = EditorFocused(cx.handle());
5933 cx.emit_global(focused_event);
5934 if let Some(rename) = self.pending_rename.as_ref() {
5935 cx.focus(&rename.editor);
5936 } else {
5937 self.focused = true;
5938 self.blink_cursors(self.blink_epoch, cx);
5939 self.buffer.update(cx, |buffer, cx| {
5940 buffer.finalize_last_transaction(cx);
5941 if self.leader_replica_id.is_none() {
5942 buffer.set_active_selections(
5943 &self.selections.disjoint_anchors(),
5944 self.selections.line_mode,
5945 cx,
5946 );
5947 }
5948 });
5949 }
5950 }
5951
5952 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5953 let blurred_event = EditorBlurred(cx.handle());
5954 cx.emit_global(blurred_event);
5955 self.focused = false;
5956 self.buffer
5957 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5958 self.hide_context_menu(cx);
5959 cx.emit(Event::Blurred);
5960 cx.notify();
5961 }
5962
5963 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5964 let mut context = Self::default_keymap_context();
5965 let mode = match self.mode {
5966 EditorMode::SingleLine => "single_line",
5967 EditorMode::AutoHeight { .. } => "auto_height",
5968 EditorMode::Full => "full",
5969 };
5970 context.map.insert("mode".into(), mode.into());
5971 if self.pending_rename.is_some() {
5972 context.set.insert("renaming".into());
5973 }
5974 match self.context_menu.as_ref() {
5975 Some(ContextMenu::Completions(_)) => {
5976 context.set.insert("showing_completions".into());
5977 }
5978 Some(ContextMenu::CodeActions(_)) => {
5979 context.set.insert("showing_code_actions".into());
5980 }
5981 None => {}
5982 }
5983
5984 for layer in self.keymap_context_layers.values() {
5985 context.extend(layer);
5986 }
5987
5988 context
5989 }
5990}
5991
5992fn build_style(
5993 settings: &Settings,
5994 get_field_editor_theme: Option<GetFieldEditorTheme>,
5995 override_text_style: Option<&OverrideTextStyle>,
5996 cx: &AppContext,
5997) -> EditorStyle {
5998 let font_cache = cx.font_cache();
5999
6000 let mut theme = settings.theme.editor.clone();
6001 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6002 let field_editor_theme = get_field_editor_theme(&settings.theme);
6003 theme.text_color = field_editor_theme.text.color;
6004 theme.selection = field_editor_theme.selection;
6005 theme.background = field_editor_theme
6006 .container
6007 .background_color
6008 .unwrap_or_default();
6009 EditorStyle {
6010 text: field_editor_theme.text,
6011 placeholder_text: field_editor_theme.placeholder_text,
6012 theme,
6013 }
6014 } else {
6015 let font_family_id = settings.buffer_font_family;
6016 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6017 let font_properties = Default::default();
6018 let font_id = font_cache
6019 .select_font(font_family_id, &font_properties)
6020 .unwrap();
6021 let font_size = settings.buffer_font_size;
6022 EditorStyle {
6023 text: TextStyle {
6024 color: settings.theme.editor.text_color,
6025 font_family_name,
6026 font_family_id,
6027 font_id,
6028 font_size,
6029 font_properties,
6030 underline: Default::default(),
6031 },
6032 placeholder_text: None,
6033 theme,
6034 }
6035 };
6036
6037 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6038 if let Some(highlighted) = style
6039 .text
6040 .clone()
6041 .highlight(highlight_style, font_cache)
6042 .log_err()
6043 {
6044 style.text = highlighted;
6045 }
6046 }
6047
6048 style
6049}
6050
6051trait SelectionExt {
6052 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6053 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6054 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6055 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6056 -> Range<u32>;
6057}
6058
6059impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6060 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6061 let start = self.start.to_point(buffer);
6062 let end = self.end.to_point(buffer);
6063 if self.reversed {
6064 end..start
6065 } else {
6066 start..end
6067 }
6068 }
6069
6070 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6071 let start = self.start.to_offset(buffer);
6072 let end = self.end.to_offset(buffer);
6073 if self.reversed {
6074 end..start
6075 } else {
6076 start..end
6077 }
6078 }
6079
6080 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6081 let start = self
6082 .start
6083 .to_point(&map.buffer_snapshot)
6084 .to_display_point(map);
6085 let end = self
6086 .end
6087 .to_point(&map.buffer_snapshot)
6088 .to_display_point(map);
6089 if self.reversed {
6090 end..start
6091 } else {
6092 start..end
6093 }
6094 }
6095
6096 fn spanned_rows(
6097 &self,
6098 include_end_if_at_line_start: bool,
6099 map: &DisplaySnapshot,
6100 ) -> Range<u32> {
6101 let start = self.start.to_point(&map.buffer_snapshot);
6102 let mut end = self.end.to_point(&map.buffer_snapshot);
6103 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6104 end.row -= 1;
6105 }
6106
6107 let buffer_start = map.prev_line_boundary(start).0;
6108 let buffer_end = map.next_line_boundary(end).0;
6109 buffer_start.row..buffer_end.row + 1
6110 }
6111}
6112
6113impl<T: InvalidationRegion> InvalidationStack<T> {
6114 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6115 where
6116 S: Clone + ToOffset,
6117 {
6118 while let Some(region) = self.last() {
6119 let all_selections_inside_invalidation_ranges =
6120 if selections.len() == region.ranges().len() {
6121 selections
6122 .iter()
6123 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6124 .all(|(selection, invalidation_range)| {
6125 let head = selection.head().to_offset(&buffer);
6126 invalidation_range.start <= head && invalidation_range.end >= head
6127 })
6128 } else {
6129 false
6130 };
6131
6132 if all_selections_inside_invalidation_ranges {
6133 break;
6134 } else {
6135 self.pop();
6136 }
6137 }
6138 }
6139}
6140
6141impl<T> Default for InvalidationStack<T> {
6142 fn default() -> Self {
6143 Self(Default::default())
6144 }
6145}
6146
6147impl<T> Deref for InvalidationStack<T> {
6148 type Target = Vec<T>;
6149
6150 fn deref(&self) -> &Self::Target {
6151 &self.0
6152 }
6153}
6154
6155impl<T> DerefMut for InvalidationStack<T> {
6156 fn deref_mut(&mut self) -> &mut Self::Target {
6157 &mut self.0
6158 }
6159}
6160
6161impl InvalidationRegion for BracketPairState {
6162 fn ranges(&self) -> &[Range<Anchor>] {
6163 &self.ranges
6164 }
6165}
6166
6167impl InvalidationRegion for SnippetState {
6168 fn ranges(&self) -> &[Range<Anchor>] {
6169 &self.ranges[self.active_index]
6170 }
6171}
6172
6173impl Deref for EditorStyle {
6174 type Target = theme::Editor;
6175
6176 fn deref(&self) -> &Self::Target {
6177 &self.theme
6178 }
6179}
6180
6181pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6182 let mut highlighted_lines = Vec::new();
6183 for line in diagnostic.message.lines() {
6184 highlighted_lines.push(highlight_diagnostic_message(line));
6185 }
6186
6187 Arc::new(move |cx: &mut BlockContext| {
6188 let settings = cx.global::<Settings>();
6189 let theme = &settings.theme.editor;
6190 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6191 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6192 Flex::column()
6193 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6194 Label::new(
6195 line.clone(),
6196 style.message.clone().with_font_size(font_size),
6197 )
6198 .with_highlights(highlights.clone())
6199 .contained()
6200 .with_margin_left(cx.anchor_x)
6201 .boxed()
6202 }))
6203 .aligned()
6204 .left()
6205 .boxed()
6206 })
6207}
6208
6209pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6210 let mut message_without_backticks = String::new();
6211 let mut prev_offset = 0;
6212 let mut inside_block = false;
6213 let mut highlights = Vec::new();
6214 for (match_ix, (offset, _)) in message
6215 .match_indices('`')
6216 .chain([(message.len(), "")])
6217 .enumerate()
6218 {
6219 message_without_backticks.push_str(&message[prev_offset..offset]);
6220 if inside_block {
6221 highlights.extend(prev_offset - match_ix..offset - match_ix);
6222 }
6223
6224 inside_block = !inside_block;
6225 prev_offset = offset + 1;
6226 }
6227
6228 (message_without_backticks, highlights)
6229}
6230
6231pub fn diagnostic_style(
6232 severity: DiagnosticSeverity,
6233 valid: bool,
6234 theme: &theme::Editor,
6235) -> DiagnosticStyle {
6236 match (severity, valid) {
6237 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6238 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6239 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6240 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6241 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6242 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6243 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6244 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6245 _ => theme.invalid_hint_diagnostic.clone(),
6246 }
6247}
6248
6249pub fn combine_syntax_and_fuzzy_match_highlights(
6250 text: &str,
6251 default_style: HighlightStyle,
6252 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6253 match_indices: &[usize],
6254) -> Vec<(Range<usize>, HighlightStyle)> {
6255 let mut result = Vec::new();
6256 let mut match_indices = match_indices.iter().copied().peekable();
6257
6258 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6259 {
6260 syntax_highlight.weight = None;
6261
6262 // Add highlights for any fuzzy match characters before the next
6263 // syntax highlight range.
6264 while let Some(&match_index) = match_indices.peek() {
6265 if match_index >= range.start {
6266 break;
6267 }
6268 match_indices.next();
6269 let end_index = char_ix_after(match_index, text);
6270 let mut match_style = default_style;
6271 match_style.weight = Some(fonts::Weight::BOLD);
6272 result.push((match_index..end_index, match_style));
6273 }
6274
6275 if range.start == usize::MAX {
6276 break;
6277 }
6278
6279 // Add highlights for any fuzzy match characters within the
6280 // syntax highlight range.
6281 let mut offset = range.start;
6282 while let Some(&match_index) = match_indices.peek() {
6283 if match_index >= range.end {
6284 break;
6285 }
6286
6287 match_indices.next();
6288 if match_index > offset {
6289 result.push((offset..match_index, syntax_highlight));
6290 }
6291
6292 let mut end_index = char_ix_after(match_index, text);
6293 while let Some(&next_match_index) = match_indices.peek() {
6294 if next_match_index == end_index && next_match_index < range.end {
6295 end_index = char_ix_after(next_match_index, text);
6296 match_indices.next();
6297 } else {
6298 break;
6299 }
6300 }
6301
6302 let mut match_style = syntax_highlight;
6303 match_style.weight = Some(fonts::Weight::BOLD);
6304 result.push((match_index..end_index, match_style));
6305 offset = end_index;
6306 }
6307
6308 if offset < range.end {
6309 result.push((offset..range.end, syntax_highlight));
6310 }
6311 }
6312
6313 fn char_ix_after(ix: usize, text: &str) -> usize {
6314 ix + text[ix..].chars().next().unwrap().len_utf8()
6315 }
6316
6317 result
6318}
6319
6320pub fn styled_runs_for_code_label<'a>(
6321 label: &'a CodeLabel,
6322 syntax_theme: &'a theme::SyntaxTheme,
6323) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6324 let fade_out = HighlightStyle {
6325 fade_out: Some(0.35),
6326 ..Default::default()
6327 };
6328
6329 let mut prev_end = label.filter_range.end;
6330 label
6331 .runs
6332 .iter()
6333 .enumerate()
6334 .flat_map(move |(ix, (range, highlight_id))| {
6335 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6336 style
6337 } else {
6338 return Default::default();
6339 };
6340 let mut muted_style = style.clone();
6341 muted_style.highlight(fade_out);
6342
6343 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6344 if range.start >= label.filter_range.end {
6345 if range.start > prev_end {
6346 runs.push((prev_end..range.start, fade_out));
6347 }
6348 runs.push((range.clone(), muted_style));
6349 } else if range.end <= label.filter_range.end {
6350 runs.push((range.clone(), style));
6351 } else {
6352 runs.push((range.start..label.filter_range.end, style));
6353 runs.push((label.filter_range.end..range.end, muted_style));
6354 }
6355 prev_end = cmp::max(prev_end, range.end);
6356
6357 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6358 runs.push((prev_end..label.text.len(), fade_out));
6359 }
6360
6361 runs
6362 })
6363}
6364
6365#[cfg(test)]
6366mod tests {
6367 use crate::test::{
6368 assert_text_with_selections, build_editor, select_ranges, EditorTestContext,
6369 };
6370
6371 use super::*;
6372 use gpui::{
6373 geometry::rect::RectF,
6374 platform::{WindowBounds, WindowOptions},
6375 };
6376 use indoc::indoc;
6377 use language::{FakeLspAdapter, LanguageConfig};
6378 use lsp::FakeLanguageServer;
6379 use project::FakeFs;
6380 use settings::LanguageOverride;
6381 use smol::stream::StreamExt;
6382 use std::{cell::RefCell, rc::Rc, time::Instant};
6383 use text::Point;
6384 use unindent::Unindent;
6385 use util::{
6386 assert_set_eq,
6387 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6388 };
6389 use workspace::{FollowableItem, ItemHandle};
6390
6391 #[gpui::test]
6392 fn test_edit_events(cx: &mut MutableAppContext) {
6393 cx.set_global(Settings::test(cx));
6394 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6395
6396 let events = Rc::new(RefCell::new(Vec::new()));
6397 let (_, editor1) = cx.add_window(Default::default(), {
6398 let events = events.clone();
6399 |cx| {
6400 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6401 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6402 events.borrow_mut().push(("editor1", *event));
6403 }
6404 })
6405 .detach();
6406 Editor::for_buffer(buffer.clone(), None, cx)
6407 }
6408 });
6409 let (_, editor2) = cx.add_window(Default::default(), {
6410 let events = events.clone();
6411 |cx| {
6412 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6413 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6414 events.borrow_mut().push(("editor2", *event));
6415 }
6416 })
6417 .detach();
6418 Editor::for_buffer(buffer.clone(), None, cx)
6419 }
6420 });
6421 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6422
6423 // Mutating editor 1 will emit an `Edited` event only for that editor.
6424 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6425 assert_eq!(
6426 mem::take(&mut *events.borrow_mut()),
6427 [
6428 ("editor1", Event::Edited),
6429 ("editor1", Event::BufferEdited),
6430 ("editor2", Event::BufferEdited),
6431 ("editor1", Event::Dirtied),
6432 ("editor2", Event::Dirtied)
6433 ]
6434 );
6435
6436 // Mutating editor 2 will emit an `Edited` event only for that editor.
6437 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6438 assert_eq!(
6439 mem::take(&mut *events.borrow_mut()),
6440 [
6441 ("editor2", Event::Edited),
6442 ("editor1", Event::BufferEdited),
6443 ("editor2", Event::BufferEdited),
6444 ]
6445 );
6446
6447 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6448 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6449 assert_eq!(
6450 mem::take(&mut *events.borrow_mut()),
6451 [
6452 ("editor1", Event::Edited),
6453 ("editor1", Event::BufferEdited),
6454 ("editor2", Event::BufferEdited),
6455 ]
6456 );
6457
6458 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6459 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6460 assert_eq!(
6461 mem::take(&mut *events.borrow_mut()),
6462 [
6463 ("editor1", Event::Edited),
6464 ("editor1", Event::BufferEdited),
6465 ("editor2", Event::BufferEdited),
6466 ]
6467 );
6468
6469 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6470 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6471 assert_eq!(
6472 mem::take(&mut *events.borrow_mut()),
6473 [
6474 ("editor2", Event::Edited),
6475 ("editor1", Event::BufferEdited),
6476 ("editor2", Event::BufferEdited),
6477 ]
6478 );
6479
6480 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6481 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6482 assert_eq!(
6483 mem::take(&mut *events.borrow_mut()),
6484 [
6485 ("editor2", Event::Edited),
6486 ("editor1", Event::BufferEdited),
6487 ("editor2", Event::BufferEdited),
6488 ]
6489 );
6490
6491 // No event is emitted when the mutation is a no-op.
6492 editor2.update(cx, |editor, cx| {
6493 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6494
6495 editor.backspace(&Backspace, cx);
6496 });
6497 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6498 }
6499
6500 #[gpui::test]
6501 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6502 cx.set_global(Settings::test(cx));
6503 let mut now = Instant::now();
6504 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6505 let group_interval = buffer.read(cx).transaction_group_interval();
6506 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6507 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6508
6509 editor.update(cx, |editor, cx| {
6510 editor.start_transaction_at(now, cx);
6511 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6512
6513 editor.insert("cd", cx);
6514 editor.end_transaction_at(now, cx);
6515 assert_eq!(editor.text(cx), "12cd56");
6516 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6517
6518 editor.start_transaction_at(now, cx);
6519 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6520 editor.insert("e", cx);
6521 editor.end_transaction_at(now, cx);
6522 assert_eq!(editor.text(cx), "12cde6");
6523 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6524
6525 now += group_interval + Duration::from_millis(1);
6526 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6527
6528 // Simulate an edit in another editor
6529 buffer.update(cx, |buffer, cx| {
6530 buffer.start_transaction_at(now, cx);
6531 buffer.edit([(0..1, "a")], cx);
6532 buffer.edit([(1..1, "b")], cx);
6533 buffer.end_transaction_at(now, cx);
6534 });
6535
6536 assert_eq!(editor.text(cx), "ab2cde6");
6537 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6538
6539 // Last transaction happened past the group interval in a different editor.
6540 // Undo it individually and don't restore selections.
6541 editor.undo(&Undo, cx);
6542 assert_eq!(editor.text(cx), "12cde6");
6543 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6544
6545 // First two transactions happened within the group interval in this editor.
6546 // Undo them together and restore selections.
6547 editor.undo(&Undo, cx);
6548 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6549 assert_eq!(editor.text(cx), "123456");
6550 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6551
6552 // Redo the first two transactions together.
6553 editor.redo(&Redo, cx);
6554 assert_eq!(editor.text(cx), "12cde6");
6555 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6556
6557 // Redo the last transaction on its own.
6558 editor.redo(&Redo, cx);
6559 assert_eq!(editor.text(cx), "ab2cde6");
6560 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6561
6562 // Test empty transactions.
6563 editor.start_transaction_at(now, cx);
6564 editor.end_transaction_at(now, cx);
6565 editor.undo(&Undo, cx);
6566 assert_eq!(editor.text(cx), "12cde6");
6567 });
6568 }
6569
6570 #[gpui::test]
6571 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6572 cx.set_global(Settings::test(cx));
6573
6574 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6575 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6576 editor.update(cx, |view, cx| {
6577 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6578 });
6579 assert_eq!(
6580 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6581 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6582 );
6583
6584 editor.update(cx, |view, cx| {
6585 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6586 });
6587
6588 assert_eq!(
6589 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6590 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6591 );
6592
6593 editor.update(cx, |view, cx| {
6594 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6595 });
6596
6597 assert_eq!(
6598 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6599 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6600 );
6601
6602 editor.update(cx, |view, cx| {
6603 view.end_selection(cx);
6604 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6605 });
6606
6607 assert_eq!(
6608 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6609 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6610 );
6611
6612 editor.update(cx, |view, cx| {
6613 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6614 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6615 });
6616
6617 assert_eq!(
6618 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6619 [
6620 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6621 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6622 ]
6623 );
6624
6625 editor.update(cx, |view, cx| {
6626 view.end_selection(cx);
6627 });
6628
6629 assert_eq!(
6630 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6631 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6632 );
6633 }
6634
6635 #[gpui::test]
6636 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6637 cx.set_global(Settings::test(cx));
6638 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6639 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6640
6641 view.update(cx, |view, cx| {
6642 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6643 assert_eq!(
6644 view.selections.display_ranges(cx),
6645 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6646 );
6647 });
6648
6649 view.update(cx, |view, cx| {
6650 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6651 assert_eq!(
6652 view.selections.display_ranges(cx),
6653 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6654 );
6655 });
6656
6657 view.update(cx, |view, cx| {
6658 view.cancel(&Cancel, cx);
6659 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6660 assert_eq!(
6661 view.selections.display_ranges(cx),
6662 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6663 );
6664 });
6665 }
6666
6667 #[gpui::test]
6668 fn test_clone_with_selections(cx: &mut gpui::MutableAppContext) {
6669 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6670 The qu[ick brown
6671 fox jum]ps over
6672 the lazy dog
6673 "});
6674 cx.set_global(Settings::test(cx));
6675 let buffer = MultiBuffer::build_simple(&text, cx);
6676
6677 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6678
6679 let cloned_editor = view.update(cx, |view, cx| {
6680 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6681 view.clone(cx)
6682 });
6683
6684 assert_set_eq!(cloned_editor.selections.ranges(cx), selection_ranges);
6685 }
6686
6687 #[gpui::test]
6688 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6689 cx.set_global(Settings::test(cx));
6690 use workspace::Item;
6691 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6692 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6693
6694 cx.add_window(Default::default(), |cx| {
6695 let mut editor = build_editor(buffer.clone(), cx);
6696 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6697
6698 // Move the cursor a small distance.
6699 // Nothing is added to the navigation history.
6700 editor.change_selections(None, cx, |s| {
6701 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6702 });
6703 editor.change_selections(None, cx, |s| {
6704 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6705 });
6706 assert!(nav_history.borrow_mut().pop_backward().is_none());
6707
6708 // Move the cursor a large distance.
6709 // The history can jump back to the previous position.
6710 editor.change_selections(None, cx, |s| {
6711 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6712 });
6713 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6714 editor.navigate(nav_entry.data.unwrap(), cx);
6715 assert_eq!(nav_entry.item.id(), cx.view_id());
6716 assert_eq!(
6717 editor.selections.display_ranges(cx),
6718 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6719 );
6720 assert!(nav_history.borrow_mut().pop_backward().is_none());
6721
6722 // Move the cursor a small distance via the mouse.
6723 // Nothing is added to the navigation history.
6724 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6725 editor.end_selection(cx);
6726 assert_eq!(
6727 editor.selections.display_ranges(cx),
6728 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6729 );
6730 assert!(nav_history.borrow_mut().pop_backward().is_none());
6731
6732 // Move the cursor a large distance via the mouse.
6733 // The history can jump back to the previous position.
6734 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6735 editor.end_selection(cx);
6736 assert_eq!(
6737 editor.selections.display_ranges(cx),
6738 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6739 );
6740 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6741 editor.navigate(nav_entry.data.unwrap(), cx);
6742 assert_eq!(nav_entry.item.id(), cx.view_id());
6743 assert_eq!(
6744 editor.selections.display_ranges(cx),
6745 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6746 );
6747 assert!(nav_history.borrow_mut().pop_backward().is_none());
6748
6749 // Set scroll position to check later
6750 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6751 let original_scroll_position = editor.scroll_position;
6752 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6753
6754 // Jump to the end of the document and adjust scroll
6755 editor.move_to_end(&MoveToEnd, cx);
6756 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6757 assert_ne!(editor.scroll_position, original_scroll_position);
6758 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6759
6760 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6761 editor.navigate(nav_entry.data.unwrap(), cx);
6762 assert_eq!(editor.scroll_position, original_scroll_position);
6763 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6764
6765 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6766 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6767 invalid_anchor.text_anchor.buffer_id = Some(999);
6768 let invalid_point = Point::new(9999, 0);
6769 editor.navigate(
6770 Box::new(NavigationData {
6771 cursor_anchor: invalid_anchor.clone(),
6772 cursor_position: invalid_point,
6773 scroll_top_anchor: invalid_anchor.clone(),
6774 scroll_top_row: invalid_point.row,
6775 scroll_position: Default::default(),
6776 }),
6777 cx,
6778 );
6779 assert_eq!(
6780 editor.selections.display_ranges(cx),
6781 &[editor.max_point(cx)..editor.max_point(cx)]
6782 );
6783 assert_eq!(
6784 editor.scroll_position(cx),
6785 vec2f(0., editor.max_point(cx).row() as f32)
6786 );
6787
6788 editor
6789 });
6790 }
6791
6792 #[gpui::test]
6793 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6794 cx.set_global(Settings::test(cx));
6795 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6796 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6797
6798 view.update(cx, |view, cx| {
6799 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6800 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6801 view.end_selection(cx);
6802
6803 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6804 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6805 view.end_selection(cx);
6806 assert_eq!(
6807 view.selections.display_ranges(cx),
6808 [
6809 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6810 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6811 ]
6812 );
6813 });
6814
6815 view.update(cx, |view, cx| {
6816 view.cancel(&Cancel, cx);
6817 assert_eq!(
6818 view.selections.display_ranges(cx),
6819 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6820 );
6821 });
6822
6823 view.update(cx, |view, cx| {
6824 view.cancel(&Cancel, cx);
6825 assert_eq!(
6826 view.selections.display_ranges(cx),
6827 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6828 );
6829 });
6830 }
6831
6832 #[gpui::test]
6833 fn test_fold(cx: &mut gpui::MutableAppContext) {
6834 cx.set_global(Settings::test(cx));
6835 let buffer = MultiBuffer::build_simple(
6836 &"
6837 impl Foo {
6838 // Hello!
6839
6840 fn a() {
6841 1
6842 }
6843
6844 fn b() {
6845 2
6846 }
6847
6848 fn c() {
6849 3
6850 }
6851 }
6852 "
6853 .unindent(),
6854 cx,
6855 );
6856 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6857
6858 view.update(cx, |view, cx| {
6859 view.change_selections(None, cx, |s| {
6860 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6861 });
6862 view.fold(&Fold, cx);
6863 assert_eq!(
6864 view.display_text(cx),
6865 "
6866 impl Foo {
6867 // Hello!
6868
6869 fn a() {
6870 1
6871 }
6872
6873 fn b() {…
6874 }
6875
6876 fn c() {…
6877 }
6878 }
6879 "
6880 .unindent(),
6881 );
6882
6883 view.fold(&Fold, cx);
6884 assert_eq!(
6885 view.display_text(cx),
6886 "
6887 impl Foo {…
6888 }
6889 "
6890 .unindent(),
6891 );
6892
6893 view.unfold_lines(&UnfoldLines, cx);
6894 assert_eq!(
6895 view.display_text(cx),
6896 "
6897 impl Foo {
6898 // Hello!
6899
6900 fn a() {
6901 1
6902 }
6903
6904 fn b() {…
6905 }
6906
6907 fn c() {…
6908 }
6909 }
6910 "
6911 .unindent(),
6912 );
6913
6914 view.unfold_lines(&UnfoldLines, cx);
6915 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6916 });
6917 }
6918
6919 #[gpui::test]
6920 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6921 cx.set_global(Settings::test(cx));
6922 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6923 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6924
6925 buffer.update(cx, |buffer, cx| {
6926 buffer.edit(
6927 vec![
6928 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6929 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6930 ],
6931 cx,
6932 );
6933 });
6934
6935 view.update(cx, |view, cx| {
6936 assert_eq!(
6937 view.selections.display_ranges(cx),
6938 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6939 );
6940
6941 view.move_down(&MoveDown, cx);
6942 assert_eq!(
6943 view.selections.display_ranges(cx),
6944 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6945 );
6946
6947 view.move_right(&MoveRight, cx);
6948 assert_eq!(
6949 view.selections.display_ranges(cx),
6950 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6951 );
6952
6953 view.move_left(&MoveLeft, cx);
6954 assert_eq!(
6955 view.selections.display_ranges(cx),
6956 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6957 );
6958
6959 view.move_up(&MoveUp, cx);
6960 assert_eq!(
6961 view.selections.display_ranges(cx),
6962 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6963 );
6964
6965 view.move_to_end(&MoveToEnd, cx);
6966 assert_eq!(
6967 view.selections.display_ranges(cx),
6968 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6969 );
6970
6971 view.move_to_beginning(&MoveToBeginning, cx);
6972 assert_eq!(
6973 view.selections.display_ranges(cx),
6974 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6975 );
6976
6977 view.change_selections(None, cx, |s| {
6978 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6979 });
6980 view.select_to_beginning(&SelectToBeginning, cx);
6981 assert_eq!(
6982 view.selections.display_ranges(cx),
6983 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6984 );
6985
6986 view.select_to_end(&SelectToEnd, cx);
6987 assert_eq!(
6988 view.selections.display_ranges(cx),
6989 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6990 );
6991 });
6992 }
6993
6994 #[gpui::test]
6995 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6996 cx.set_global(Settings::test(cx));
6997 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6998 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6999
7000 assert_eq!('ⓐ'.len_utf8(), 3);
7001 assert_eq!('α'.len_utf8(), 2);
7002
7003 view.update(cx, |view, cx| {
7004 view.fold_ranges(
7005 vec![
7006 Point::new(0, 6)..Point::new(0, 12),
7007 Point::new(1, 2)..Point::new(1, 4),
7008 Point::new(2, 4)..Point::new(2, 8),
7009 ],
7010 cx,
7011 );
7012 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7013
7014 view.move_right(&MoveRight, cx);
7015 assert_eq!(
7016 view.selections.display_ranges(cx),
7017 &[empty_range(0, "ⓐ".len())]
7018 );
7019 view.move_right(&MoveRight, cx);
7020 assert_eq!(
7021 view.selections.display_ranges(cx),
7022 &[empty_range(0, "ⓐⓑ".len())]
7023 );
7024 view.move_right(&MoveRight, cx);
7025 assert_eq!(
7026 view.selections.display_ranges(cx),
7027 &[empty_range(0, "ⓐⓑ…".len())]
7028 );
7029
7030 view.move_down(&MoveDown, cx);
7031 assert_eq!(
7032 view.selections.display_ranges(cx),
7033 &[empty_range(1, "ab…".len())]
7034 );
7035 view.move_left(&MoveLeft, cx);
7036 assert_eq!(
7037 view.selections.display_ranges(cx),
7038 &[empty_range(1, "ab".len())]
7039 );
7040 view.move_left(&MoveLeft, cx);
7041 assert_eq!(
7042 view.selections.display_ranges(cx),
7043 &[empty_range(1, "a".len())]
7044 );
7045
7046 view.move_down(&MoveDown, cx);
7047 assert_eq!(
7048 view.selections.display_ranges(cx),
7049 &[empty_range(2, "α".len())]
7050 );
7051 view.move_right(&MoveRight, cx);
7052 assert_eq!(
7053 view.selections.display_ranges(cx),
7054 &[empty_range(2, "αβ".len())]
7055 );
7056 view.move_right(&MoveRight, cx);
7057 assert_eq!(
7058 view.selections.display_ranges(cx),
7059 &[empty_range(2, "αβ…".len())]
7060 );
7061 view.move_right(&MoveRight, cx);
7062 assert_eq!(
7063 view.selections.display_ranges(cx),
7064 &[empty_range(2, "αβ…ε".len())]
7065 );
7066
7067 view.move_up(&MoveUp, cx);
7068 assert_eq!(
7069 view.selections.display_ranges(cx),
7070 &[empty_range(1, "ab…e".len())]
7071 );
7072 view.move_up(&MoveUp, cx);
7073 assert_eq!(
7074 view.selections.display_ranges(cx),
7075 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7076 );
7077 view.move_left(&MoveLeft, cx);
7078 assert_eq!(
7079 view.selections.display_ranges(cx),
7080 &[empty_range(0, "ⓐⓑ…".len())]
7081 );
7082 view.move_left(&MoveLeft, cx);
7083 assert_eq!(
7084 view.selections.display_ranges(cx),
7085 &[empty_range(0, "ⓐⓑ".len())]
7086 );
7087 view.move_left(&MoveLeft, cx);
7088 assert_eq!(
7089 view.selections.display_ranges(cx),
7090 &[empty_range(0, "ⓐ".len())]
7091 );
7092 });
7093 }
7094
7095 #[gpui::test]
7096 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7097 cx.set_global(Settings::test(cx));
7098 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7099 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7100 view.update(cx, |view, cx| {
7101 view.change_selections(None, cx, |s| {
7102 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7103 });
7104 view.move_down(&MoveDown, cx);
7105 assert_eq!(
7106 view.selections.display_ranges(cx),
7107 &[empty_range(1, "abcd".len())]
7108 );
7109
7110 view.move_down(&MoveDown, cx);
7111 assert_eq!(
7112 view.selections.display_ranges(cx),
7113 &[empty_range(2, "αβγ".len())]
7114 );
7115
7116 view.move_down(&MoveDown, cx);
7117 assert_eq!(
7118 view.selections.display_ranges(cx),
7119 &[empty_range(3, "abcd".len())]
7120 );
7121
7122 view.move_down(&MoveDown, cx);
7123 assert_eq!(
7124 view.selections.display_ranges(cx),
7125 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7126 );
7127
7128 view.move_up(&MoveUp, cx);
7129 assert_eq!(
7130 view.selections.display_ranges(cx),
7131 &[empty_range(3, "abcd".len())]
7132 );
7133
7134 view.move_up(&MoveUp, cx);
7135 assert_eq!(
7136 view.selections.display_ranges(cx),
7137 &[empty_range(2, "αβγ".len())]
7138 );
7139 });
7140 }
7141
7142 #[gpui::test]
7143 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7144 cx.set_global(Settings::test(cx));
7145 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7146 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7147 view.update(cx, |view, cx| {
7148 view.change_selections(None, cx, |s| {
7149 s.select_display_ranges([
7150 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7151 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7152 ]);
7153 });
7154 });
7155
7156 view.update(cx, |view, cx| {
7157 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7158 assert_eq!(
7159 view.selections.display_ranges(cx),
7160 &[
7161 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7162 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7163 ]
7164 );
7165 });
7166
7167 view.update(cx, |view, cx| {
7168 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7169 assert_eq!(
7170 view.selections.display_ranges(cx),
7171 &[
7172 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7173 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7174 ]
7175 );
7176 });
7177
7178 view.update(cx, |view, cx| {
7179 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7180 assert_eq!(
7181 view.selections.display_ranges(cx),
7182 &[
7183 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7184 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7185 ]
7186 );
7187 });
7188
7189 view.update(cx, |view, cx| {
7190 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7191 assert_eq!(
7192 view.selections.display_ranges(cx),
7193 &[
7194 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7195 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7196 ]
7197 );
7198 });
7199
7200 // Moving to the end of line again is a no-op.
7201 view.update(cx, |view, cx| {
7202 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7203 assert_eq!(
7204 view.selections.display_ranges(cx),
7205 &[
7206 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7207 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7208 ]
7209 );
7210 });
7211
7212 view.update(cx, |view, cx| {
7213 view.move_left(&MoveLeft, cx);
7214 view.select_to_beginning_of_line(
7215 &SelectToBeginningOfLine {
7216 stop_at_soft_wraps: true,
7217 },
7218 cx,
7219 );
7220 assert_eq!(
7221 view.selections.display_ranges(cx),
7222 &[
7223 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7224 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7225 ]
7226 );
7227 });
7228
7229 view.update(cx, |view, cx| {
7230 view.select_to_beginning_of_line(
7231 &SelectToBeginningOfLine {
7232 stop_at_soft_wraps: true,
7233 },
7234 cx,
7235 );
7236 assert_eq!(
7237 view.selections.display_ranges(cx),
7238 &[
7239 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7240 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7241 ]
7242 );
7243 });
7244
7245 view.update(cx, |view, cx| {
7246 view.select_to_beginning_of_line(
7247 &SelectToBeginningOfLine {
7248 stop_at_soft_wraps: true,
7249 },
7250 cx,
7251 );
7252 assert_eq!(
7253 view.selections.display_ranges(cx),
7254 &[
7255 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7256 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7257 ]
7258 );
7259 });
7260
7261 view.update(cx, |view, cx| {
7262 view.select_to_end_of_line(
7263 &SelectToEndOfLine {
7264 stop_at_soft_wraps: true,
7265 },
7266 cx,
7267 );
7268 assert_eq!(
7269 view.selections.display_ranges(cx),
7270 &[
7271 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7272 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7273 ]
7274 );
7275 });
7276
7277 view.update(cx, |view, cx| {
7278 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7279 assert_eq!(view.display_text(cx), "ab\n de");
7280 assert_eq!(
7281 view.selections.display_ranges(cx),
7282 &[
7283 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7284 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7285 ]
7286 );
7287 });
7288
7289 view.update(cx, |view, cx| {
7290 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7291 assert_eq!(view.display_text(cx), "\n");
7292 assert_eq!(
7293 view.selections.display_ranges(cx),
7294 &[
7295 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7296 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7297 ]
7298 );
7299 });
7300 }
7301
7302 #[gpui::test]
7303 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7304 cx.set_global(Settings::test(cx));
7305 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7306 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7307 view.update(cx, |view, cx| {
7308 view.change_selections(None, cx, |s| {
7309 s.select_display_ranges([
7310 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7311 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7312 ])
7313 });
7314
7315 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7316 assert_selection_ranges(
7317 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7318 vec![('<', '>'), ('[', ']')],
7319 view,
7320 cx,
7321 );
7322
7323 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7324 assert_selection_ranges(
7325 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7326 vec![('<', '>'), ('[', ']')],
7327 view,
7328 cx,
7329 );
7330
7331 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7332 assert_selection_ranges(
7333 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7334 vec![('<', '>'), ('[', ']')],
7335 view,
7336 cx,
7337 );
7338
7339 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7340 assert_selection_ranges(
7341 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7342 vec![('<', '>'), ('[', ']')],
7343 view,
7344 cx,
7345 );
7346
7347 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7348 assert_selection_ranges(
7349 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7350 vec![('<', '>'), ('[', ']')],
7351 view,
7352 cx,
7353 );
7354
7355 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7356 assert_selection_ranges(
7357 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7358 vec![('<', '>'), ('[', ']')],
7359 view,
7360 cx,
7361 );
7362
7363 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7364 assert_selection_ranges(
7365 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7366 vec![('<', '>'), ('[', ']')],
7367 view,
7368 cx,
7369 );
7370
7371 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7372 assert_selection_ranges(
7373 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7374 vec![('<', '>'), ('[', ']')],
7375 view,
7376 cx,
7377 );
7378
7379 view.move_right(&MoveRight, cx);
7380 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7381 assert_selection_ranges(
7382 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7383 vec![('<', '>'), ('[', ']')],
7384 view,
7385 cx,
7386 );
7387
7388 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7389 assert_selection_ranges(
7390 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7391 vec![('<', '>'), ('[', ']')],
7392 view,
7393 cx,
7394 );
7395
7396 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7397 assert_selection_ranges(
7398 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7399 vec![('<', '>'), ('[', ']')],
7400 view,
7401 cx,
7402 );
7403 });
7404 }
7405
7406 #[gpui::test]
7407 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7408 cx.set_global(Settings::test(cx));
7409 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7410 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7411
7412 view.update(cx, |view, cx| {
7413 view.set_wrap_width(Some(140.), cx);
7414 assert_eq!(
7415 view.display_text(cx),
7416 "use one::{\n two::three::\n four::five\n};"
7417 );
7418
7419 view.change_selections(None, cx, |s| {
7420 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7421 });
7422
7423 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7424 assert_eq!(
7425 view.selections.display_ranges(cx),
7426 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7427 );
7428
7429 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7430 assert_eq!(
7431 view.selections.display_ranges(cx),
7432 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7433 );
7434
7435 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7436 assert_eq!(
7437 view.selections.display_ranges(cx),
7438 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7439 );
7440
7441 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7442 assert_eq!(
7443 view.selections.display_ranges(cx),
7444 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7445 );
7446
7447 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7448 assert_eq!(
7449 view.selections.display_ranges(cx),
7450 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7451 );
7452
7453 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7454 assert_eq!(
7455 view.selections.display_ranges(cx),
7456 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7457 );
7458 });
7459 }
7460
7461 #[gpui::test]
7462 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7463 cx.set_global(Settings::test(cx));
7464 let (text, ranges) = marked_text_ranges("one [two three] four");
7465 let buffer = MultiBuffer::build_simple(&text, cx);
7466
7467 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7468
7469 editor.update(cx, |editor, cx| {
7470 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7471 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7472 assert_eq!(editor.text(cx), " four");
7473 });
7474 }
7475
7476 #[gpui::test]
7477 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7478 cx.set_global(Settings::test(cx));
7479 let buffer = MultiBuffer::build_simple("one two three four", cx);
7480 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7481
7482 view.update(cx, |view, cx| {
7483 view.change_selections(None, cx, |s| {
7484 s.select_display_ranges([
7485 // an empty selection - the preceding word fragment is deleted
7486 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7487 // characters selected - they are deleted
7488 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7489 ])
7490 });
7491 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7492 });
7493
7494 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7495
7496 view.update(cx, |view, cx| {
7497 view.change_selections(None, cx, |s| {
7498 s.select_display_ranges([
7499 // an empty selection - the following word fragment is deleted
7500 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7501 // characters selected - they are deleted
7502 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7503 ])
7504 });
7505 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7506 });
7507
7508 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7509 }
7510
7511 #[gpui::test]
7512 fn test_newline(cx: &mut gpui::MutableAppContext) {
7513 cx.set_global(Settings::test(cx));
7514 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7515 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7516
7517 view.update(cx, |view, cx| {
7518 view.change_selections(None, cx, |s| {
7519 s.select_display_ranges([
7520 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7521 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7522 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7523 ])
7524 });
7525
7526 view.newline(&Newline, cx);
7527 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7528 });
7529 }
7530
7531 #[gpui::test]
7532 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7533 cx.set_global(Settings::test(cx));
7534 let buffer = MultiBuffer::build_simple(
7535 "
7536 a
7537 b(
7538 X
7539 )
7540 c(
7541 X
7542 )
7543 "
7544 .unindent()
7545 .as_str(),
7546 cx,
7547 );
7548
7549 let (_, editor) = cx.add_window(Default::default(), |cx| {
7550 let mut editor = build_editor(buffer.clone(), cx);
7551 editor.change_selections(None, cx, |s| {
7552 s.select_ranges([
7553 Point::new(2, 4)..Point::new(2, 5),
7554 Point::new(5, 4)..Point::new(5, 5),
7555 ])
7556 });
7557 editor
7558 });
7559
7560 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7561 buffer.update(cx, |buffer, cx| {
7562 buffer.edit(
7563 [
7564 (Point::new(1, 2)..Point::new(3, 0), ""),
7565 (Point::new(4, 2)..Point::new(6, 0), ""),
7566 ],
7567 cx,
7568 );
7569 assert_eq!(
7570 buffer.read(cx).text(),
7571 "
7572 a
7573 b()
7574 c()
7575 "
7576 .unindent()
7577 );
7578 });
7579
7580 editor.update(cx, |editor, cx| {
7581 assert_eq!(
7582 editor.selections.ranges(cx),
7583 &[
7584 Point::new(1, 2)..Point::new(1, 2),
7585 Point::new(2, 2)..Point::new(2, 2),
7586 ],
7587 );
7588
7589 editor.newline(&Newline, cx);
7590 assert_eq!(
7591 editor.text(cx),
7592 "
7593 a
7594 b(
7595 )
7596 c(
7597 )
7598 "
7599 .unindent()
7600 );
7601
7602 // The selections are moved after the inserted newlines
7603 assert_eq!(
7604 editor.selections.ranges(cx),
7605 &[
7606 Point::new(2, 0)..Point::new(2, 0),
7607 Point::new(4, 0)..Point::new(4, 0),
7608 ],
7609 );
7610 });
7611 }
7612
7613 #[gpui::test]
7614 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7615 cx.set_global(Settings::test(cx));
7616 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7617 let (_, editor) = cx.add_window(Default::default(), |cx| {
7618 let mut editor = build_editor(buffer.clone(), cx);
7619 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7620 editor
7621 });
7622
7623 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7624 buffer.update(cx, |buffer, cx| {
7625 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7626 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7627 });
7628
7629 editor.update(cx, |editor, cx| {
7630 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7631
7632 editor.insert("Z", cx);
7633 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7634
7635 // The selections are moved after the inserted characters
7636 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7637 });
7638 }
7639
7640 #[gpui::test]
7641 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7642 let mut cx = EditorTestContext::new(cx).await;
7643
7644 cx.set_state(indoc! {"
7645 [one} [two}
7646 three
7647 four"});
7648 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7649 cx.assert_editor_state(indoc! {"
7650 [one} [two}
7651 three
7652 four"});
7653
7654 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7655 cx.assert_editor_state(indoc! {"
7656 [one} [two}
7657 three
7658 four"});
7659
7660 // select across line ending
7661 cx.set_state(indoc! {"
7662 one two
7663 t[hree
7664 } four"});
7665 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7666 cx.assert_editor_state(indoc! {"
7667 one two
7668 t[hree
7669 } four"});
7670
7671 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7672 cx.assert_editor_state(indoc! {"
7673 one two
7674 t[hree
7675 } four"});
7676
7677 // Ensure that indenting/outdenting works when the cursor is at column 0.
7678 cx.set_state(indoc! {"
7679 one two
7680 |three
7681 four"});
7682 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7683 cx.assert_editor_state(indoc! {"
7684 one two
7685 |three
7686 four"});
7687
7688 cx.set_state(indoc! {"
7689 one two
7690 | three
7691 four"});
7692 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7693 cx.assert_editor_state(indoc! {"
7694 one two
7695 |three
7696 four"});
7697 }
7698
7699 #[gpui::test]
7700 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7701 cx.set_global(
7702 Settings::test(cx)
7703 .with_overrides(
7704 "TOML",
7705 LanguageOverride {
7706 tab_size: Some(2),
7707 ..Default::default()
7708 },
7709 )
7710 .with_overrides(
7711 "Rust",
7712 LanguageOverride {
7713 tab_size: Some(4),
7714 ..Default::default()
7715 },
7716 ),
7717 );
7718 let toml_language = Arc::new(Language::new(
7719 LanguageConfig {
7720 name: "TOML".into(),
7721 ..Default::default()
7722 },
7723 None,
7724 ));
7725 let rust_language = Arc::new(Language::new(
7726 LanguageConfig {
7727 name: "Rust".into(),
7728 ..Default::default()
7729 },
7730 None,
7731 ));
7732
7733 let toml_buffer = cx
7734 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7735 let rust_buffer = cx.add_model(|cx| {
7736 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7737 });
7738 let multibuffer = cx.add_model(|cx| {
7739 let mut multibuffer = MultiBuffer::new(0);
7740 multibuffer.push_excerpts(
7741 toml_buffer.clone(),
7742 [Point::new(0, 0)..Point::new(2, 0)],
7743 cx,
7744 );
7745 multibuffer.push_excerpts(
7746 rust_buffer.clone(),
7747 [Point::new(0, 0)..Point::new(1, 0)],
7748 cx,
7749 );
7750 multibuffer
7751 });
7752
7753 cx.add_window(Default::default(), |cx| {
7754 let mut editor = build_editor(multibuffer, cx);
7755
7756 assert_eq!(
7757 editor.text(cx),
7758 indoc! {"
7759 a = 1
7760 b = 2
7761
7762 const c: usize = 3;
7763 "}
7764 );
7765
7766 select_ranges(
7767 &mut editor,
7768 indoc! {"
7769 [a] = 1
7770 b = 2
7771
7772 [const c:] usize = 3;
7773 "},
7774 cx,
7775 );
7776
7777 editor.tab(&Tab, cx);
7778 assert_text_with_selections(
7779 &mut editor,
7780 indoc! {"
7781 [a] = 1
7782 b = 2
7783
7784 [const c:] usize = 3;
7785 "},
7786 cx,
7787 );
7788 editor.tab_prev(&TabPrev, cx);
7789 assert_text_with_selections(
7790 &mut editor,
7791 indoc! {"
7792 [a] = 1
7793 b = 2
7794
7795 [const c:] usize = 3;
7796 "},
7797 cx,
7798 );
7799
7800 editor
7801 });
7802 }
7803
7804 #[gpui::test]
7805 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7806 let mut cx = EditorTestContext::new(cx).await;
7807 // Basic backspace
7808 cx.set_state(indoc! {"
7809 on|e two three
7810 fou[r} five six
7811 seven {eight nine
7812 ]ten"});
7813 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7814 cx.assert_editor_state(indoc! {"
7815 o|e two three
7816 fou| five six
7817 seven |ten"});
7818
7819 // Test backspace inside and around indents
7820 cx.set_state(indoc! {"
7821 zero
7822 |one
7823 |two
7824 | | | three
7825 | | four"});
7826 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7827 cx.assert_editor_state(indoc! {"
7828 zero
7829 |one
7830 |two
7831 | three| four"});
7832
7833 // Test backspace with line_mode set to true
7834 cx.update_editor(|e, _| e.selections.line_mode = true);
7835 cx.set_state(indoc! {"
7836 The |quick |brown
7837 fox jumps over
7838 the lazy dog
7839 |The qu[ick b}rown"});
7840 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7841 cx.assert_editor_state(indoc! {"
7842 |fox jumps over
7843 the lazy dog|"});
7844 }
7845
7846 #[gpui::test]
7847 async fn test_delete(cx: &mut gpui::TestAppContext) {
7848 let mut cx = EditorTestContext::new(cx).await;
7849
7850 cx.set_state(indoc! {"
7851 on|e two three
7852 fou[r} five six
7853 seven {eight nine
7854 ]ten"});
7855 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7856 cx.assert_editor_state(indoc! {"
7857 on| two three
7858 fou| five six
7859 seven |ten"});
7860
7861 // Test backspace with line_mode set to true
7862 cx.update_editor(|e, _| e.selections.line_mode = true);
7863 cx.set_state(indoc! {"
7864 The |quick |brown
7865 fox {jum]ps over
7866 the lazy dog
7867 |The qu[ick b}rown"});
7868 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7869 cx.assert_editor_state("|the lazy dog|");
7870 }
7871
7872 #[gpui::test]
7873 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7874 cx.set_global(Settings::test(cx));
7875 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7876 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7877 view.update(cx, |view, cx| {
7878 view.change_selections(None, cx, |s| {
7879 s.select_display_ranges([
7880 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7881 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7882 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7883 ])
7884 });
7885 view.delete_line(&DeleteLine, cx);
7886 assert_eq!(view.display_text(cx), "ghi");
7887 assert_eq!(
7888 view.selections.display_ranges(cx),
7889 vec![
7890 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7891 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7892 ]
7893 );
7894 });
7895
7896 cx.set_global(Settings::test(cx));
7897 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7898 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7899 view.update(cx, |view, cx| {
7900 view.change_selections(None, cx, |s| {
7901 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7902 });
7903 view.delete_line(&DeleteLine, cx);
7904 assert_eq!(view.display_text(cx), "ghi\n");
7905 assert_eq!(
7906 view.selections.display_ranges(cx),
7907 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7908 );
7909 });
7910 }
7911
7912 #[gpui::test]
7913 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7914 cx.set_global(Settings::test(cx));
7915 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7916 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7917 view.update(cx, |view, cx| {
7918 view.change_selections(None, cx, |s| {
7919 s.select_display_ranges([
7920 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7921 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7922 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7923 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7924 ])
7925 });
7926 view.duplicate_line(&DuplicateLine, cx);
7927 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7928 assert_eq!(
7929 view.selections.display_ranges(cx),
7930 vec![
7931 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7932 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7933 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7934 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7935 ]
7936 );
7937 });
7938
7939 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7940 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7941 view.update(cx, |view, cx| {
7942 view.change_selections(None, cx, |s| {
7943 s.select_display_ranges([
7944 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7945 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7946 ])
7947 });
7948 view.duplicate_line(&DuplicateLine, cx);
7949 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7950 assert_eq!(
7951 view.selections.display_ranges(cx),
7952 vec![
7953 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7954 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7955 ]
7956 );
7957 });
7958 }
7959
7960 #[gpui::test]
7961 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7962 cx.set_global(Settings::test(cx));
7963 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7964 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7965 view.update(cx, |view, cx| {
7966 view.fold_ranges(
7967 vec![
7968 Point::new(0, 2)..Point::new(1, 2),
7969 Point::new(2, 3)..Point::new(4, 1),
7970 Point::new(7, 0)..Point::new(8, 4),
7971 ],
7972 cx,
7973 );
7974 view.change_selections(None, cx, |s| {
7975 s.select_display_ranges([
7976 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7977 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7978 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7979 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7980 ])
7981 });
7982 assert_eq!(
7983 view.display_text(cx),
7984 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7985 );
7986
7987 view.move_line_up(&MoveLineUp, cx);
7988 assert_eq!(
7989 view.display_text(cx),
7990 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7991 );
7992 assert_eq!(
7993 view.selections.display_ranges(cx),
7994 vec![
7995 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7996 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7997 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7998 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7999 ]
8000 );
8001 });
8002
8003 view.update(cx, |view, cx| {
8004 view.move_line_down(&MoveLineDown, cx);
8005 assert_eq!(
8006 view.display_text(cx),
8007 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8008 );
8009 assert_eq!(
8010 view.selections.display_ranges(cx),
8011 vec![
8012 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8013 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8014 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8015 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8016 ]
8017 );
8018 });
8019
8020 view.update(cx, |view, cx| {
8021 view.move_line_down(&MoveLineDown, cx);
8022 assert_eq!(
8023 view.display_text(cx),
8024 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8025 );
8026 assert_eq!(
8027 view.selections.display_ranges(cx),
8028 vec![
8029 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8030 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8031 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8032 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8033 ]
8034 );
8035 });
8036
8037 view.update(cx, |view, cx| {
8038 view.move_line_up(&MoveLineUp, cx);
8039 assert_eq!(
8040 view.display_text(cx),
8041 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8042 );
8043 assert_eq!(
8044 view.selections.display_ranges(cx),
8045 vec![
8046 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8047 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8048 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8049 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8050 ]
8051 );
8052 });
8053 }
8054
8055 #[gpui::test]
8056 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8057 cx.set_global(Settings::test(cx));
8058 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8059 let snapshot = buffer.read(cx).snapshot(cx);
8060 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8061 editor.update(cx, |editor, cx| {
8062 editor.insert_blocks(
8063 [BlockProperties {
8064 position: snapshot.anchor_after(Point::new(2, 0)),
8065 disposition: BlockDisposition::Below,
8066 height: 1,
8067 render: Arc::new(|_| Empty::new().boxed()),
8068 }],
8069 cx,
8070 );
8071 editor.change_selections(None, cx, |s| {
8072 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8073 });
8074 editor.move_line_down(&MoveLineDown, cx);
8075 });
8076 }
8077
8078 #[gpui::test]
8079 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8080 cx.set_global(Settings::test(cx));
8081
8082 cx.add_window(Default::default(), |cx| {
8083 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8084
8085 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8086 editor.transpose(&Default::default(), cx);
8087 assert_eq!(editor.text(cx), "bac");
8088 assert_eq!(editor.selections.ranges(cx), [2..2]);
8089
8090 editor.transpose(&Default::default(), cx);
8091 assert_eq!(editor.text(cx), "bca");
8092 assert_eq!(editor.selections.ranges(cx), [3..3]);
8093
8094 editor.transpose(&Default::default(), cx);
8095 assert_eq!(editor.text(cx), "bac");
8096 assert_eq!(editor.selections.ranges(cx), [3..3]);
8097
8098 editor
8099 })
8100 .1;
8101
8102 cx.add_window(Default::default(), |cx| {
8103 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8104
8105 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8106 editor.transpose(&Default::default(), cx);
8107 assert_eq!(editor.text(cx), "acb\nde");
8108 assert_eq!(editor.selections.ranges(cx), [3..3]);
8109
8110 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8111 editor.transpose(&Default::default(), cx);
8112 assert_eq!(editor.text(cx), "acbd\ne");
8113 assert_eq!(editor.selections.ranges(cx), [5..5]);
8114
8115 editor.transpose(&Default::default(), cx);
8116 assert_eq!(editor.text(cx), "acbde\n");
8117 assert_eq!(editor.selections.ranges(cx), [6..6]);
8118
8119 editor.transpose(&Default::default(), cx);
8120 assert_eq!(editor.text(cx), "acbd\ne");
8121 assert_eq!(editor.selections.ranges(cx), [6..6]);
8122
8123 editor
8124 })
8125 .1;
8126
8127 cx.add_window(Default::default(), |cx| {
8128 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8129
8130 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8131 editor.transpose(&Default::default(), cx);
8132 assert_eq!(editor.text(cx), "bacd\ne");
8133 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8134
8135 editor.transpose(&Default::default(), cx);
8136 assert_eq!(editor.text(cx), "bcade\n");
8137 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8138
8139 editor.transpose(&Default::default(), cx);
8140 assert_eq!(editor.text(cx), "bcda\ne");
8141 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8142
8143 editor.transpose(&Default::default(), cx);
8144 assert_eq!(editor.text(cx), "bcade\n");
8145 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8146
8147 editor.transpose(&Default::default(), cx);
8148 assert_eq!(editor.text(cx), "bcaed\n");
8149 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8150
8151 editor
8152 })
8153 .1;
8154
8155 cx.add_window(Default::default(), |cx| {
8156 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8157
8158 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8159 editor.transpose(&Default::default(), cx);
8160 assert_eq!(editor.text(cx), "🏀🍐✋");
8161 assert_eq!(editor.selections.ranges(cx), [8..8]);
8162
8163 editor.transpose(&Default::default(), cx);
8164 assert_eq!(editor.text(cx), "🏀✋🍐");
8165 assert_eq!(editor.selections.ranges(cx), [11..11]);
8166
8167 editor.transpose(&Default::default(), cx);
8168 assert_eq!(editor.text(cx), "🏀🍐✋");
8169 assert_eq!(editor.selections.ranges(cx), [11..11]);
8170
8171 editor
8172 })
8173 .1;
8174 }
8175
8176 #[gpui::test]
8177 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8178 let mut cx = EditorTestContext::new(cx).await;
8179
8180 cx.set_state("[one✅ }two [three }four [five }six ");
8181 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8182 cx.assert_editor_state("|two |four |six ");
8183
8184 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8185 cx.set_state("two |four |six |");
8186 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8187 cx.assert_editor_state("two one✅ |four three |six five |");
8188
8189 // Paste again but with only two cursors. Since the number of cursors doesn't
8190 // match the number of slices in the clipboard, the entire clipboard text
8191 // is pasted at each cursor.
8192 cx.set_state("|two one✅ four three six five |");
8193 cx.update_editor(|e, cx| {
8194 e.handle_input(&Input("( ".into()), cx);
8195 e.paste(&Paste, cx);
8196 e.handle_input(&Input(") ".into()), cx);
8197 });
8198 cx.assert_editor_state(indoc! {"
8199 ( one✅
8200 three
8201 five ) |two one✅ four three six five ( one✅
8202 three
8203 five ) |"});
8204
8205 // Cut with three selections, one of which is full-line.
8206 cx.set_state(indoc! {"
8207 1[2}3
8208 4|567
8209 [8}9"});
8210 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8211 cx.assert_editor_state(indoc! {"
8212 1|3
8213 |9"});
8214
8215 // Paste with three selections, noticing how the copied selection that was full-line
8216 // gets inserted before the second cursor.
8217 cx.set_state(indoc! {"
8218 1|3
8219 9|
8220 [o}ne"});
8221 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8222 cx.assert_editor_state(indoc! {"
8223 12|3
8224 4567
8225 9|
8226 8|ne"});
8227
8228 // Copy with a single cursor only, which writes the whole line into the clipboard.
8229 cx.set_state(indoc! {"
8230 The quick brown
8231 fox ju|mps over
8232 the lazy dog"});
8233 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8234 cx.assert_clipboard_content(Some("fox jumps over\n"));
8235
8236 // Paste with three selections, noticing how the copied full-line selection is inserted
8237 // before the empty selections but replaces the selection that is non-empty.
8238 cx.set_state(indoc! {"
8239 T|he quick brown
8240 [fo}x jumps over
8241 t|he lazy dog"});
8242 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8243 cx.assert_editor_state(indoc! {"
8244 fox jumps over
8245 T|he quick brown
8246 fox jumps over
8247 |x jumps over
8248 fox jumps over
8249 t|he lazy dog"});
8250 }
8251
8252 #[gpui::test]
8253 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8254 cx.set_global(Settings::test(cx));
8255 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8256 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8257 view.update(cx, |view, cx| {
8258 view.select_all(&SelectAll, cx);
8259 assert_eq!(
8260 view.selections.display_ranges(cx),
8261 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8262 );
8263 });
8264 }
8265
8266 #[gpui::test]
8267 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8268 cx.set_global(Settings::test(cx));
8269 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8270 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8271 view.update(cx, |view, cx| {
8272 view.change_selections(None, cx, |s| {
8273 s.select_display_ranges([
8274 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8275 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8276 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8277 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8278 ])
8279 });
8280 view.select_line(&SelectLine, cx);
8281 assert_eq!(
8282 view.selections.display_ranges(cx),
8283 vec![
8284 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8285 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8286 ]
8287 );
8288 });
8289
8290 view.update(cx, |view, cx| {
8291 view.select_line(&SelectLine, cx);
8292 assert_eq!(
8293 view.selections.display_ranges(cx),
8294 vec![
8295 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8296 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8297 ]
8298 );
8299 });
8300
8301 view.update(cx, |view, cx| {
8302 view.select_line(&SelectLine, cx);
8303 assert_eq!(
8304 view.selections.display_ranges(cx),
8305 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8306 );
8307 });
8308 }
8309
8310 #[gpui::test]
8311 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8312 cx.set_global(Settings::test(cx));
8313 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8314 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8315 view.update(cx, |view, cx| {
8316 view.fold_ranges(
8317 vec![
8318 Point::new(0, 2)..Point::new(1, 2),
8319 Point::new(2, 3)..Point::new(4, 1),
8320 Point::new(7, 0)..Point::new(8, 4),
8321 ],
8322 cx,
8323 );
8324 view.change_selections(None, cx, |s| {
8325 s.select_display_ranges([
8326 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8327 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8328 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8329 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8330 ])
8331 });
8332 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8333 });
8334
8335 view.update(cx, |view, cx| {
8336 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8337 assert_eq!(
8338 view.display_text(cx),
8339 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8340 );
8341 assert_eq!(
8342 view.selections.display_ranges(cx),
8343 [
8344 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8345 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8346 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8347 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8348 ]
8349 );
8350 });
8351
8352 view.update(cx, |view, cx| {
8353 view.change_selections(None, cx, |s| {
8354 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8355 });
8356 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8357 assert_eq!(
8358 view.display_text(cx),
8359 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8360 );
8361 assert_eq!(
8362 view.selections.display_ranges(cx),
8363 [
8364 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8365 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8366 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8367 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8368 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8369 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8370 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8371 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8372 ]
8373 );
8374 });
8375 }
8376
8377 #[gpui::test]
8378 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8379 cx.set_global(Settings::test(cx));
8380 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8381 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8382
8383 view.update(cx, |view, cx| {
8384 view.change_selections(None, cx, |s| {
8385 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8386 });
8387 });
8388 view.update(cx, |view, cx| {
8389 view.add_selection_above(&AddSelectionAbove, cx);
8390 assert_eq!(
8391 view.selections.display_ranges(cx),
8392 vec![
8393 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8394 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8395 ]
8396 );
8397 });
8398
8399 view.update(cx, |view, cx| {
8400 view.add_selection_above(&AddSelectionAbove, cx);
8401 assert_eq!(
8402 view.selections.display_ranges(cx),
8403 vec![
8404 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8405 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8406 ]
8407 );
8408 });
8409
8410 view.update(cx, |view, cx| {
8411 view.add_selection_below(&AddSelectionBelow, cx);
8412 assert_eq!(
8413 view.selections.display_ranges(cx),
8414 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8415 );
8416
8417 view.undo_selection(&UndoSelection, cx);
8418 assert_eq!(
8419 view.selections.display_ranges(cx),
8420 vec![
8421 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8422 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8423 ]
8424 );
8425
8426 view.redo_selection(&RedoSelection, cx);
8427 assert_eq!(
8428 view.selections.display_ranges(cx),
8429 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8430 );
8431 });
8432
8433 view.update(cx, |view, cx| {
8434 view.add_selection_below(&AddSelectionBelow, cx);
8435 assert_eq!(
8436 view.selections.display_ranges(cx),
8437 vec![
8438 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8439 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8440 ]
8441 );
8442 });
8443
8444 view.update(cx, |view, cx| {
8445 view.add_selection_below(&AddSelectionBelow, cx);
8446 assert_eq!(
8447 view.selections.display_ranges(cx),
8448 vec![
8449 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8450 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8451 ]
8452 );
8453 });
8454
8455 view.update(cx, |view, cx| {
8456 view.change_selections(None, cx, |s| {
8457 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8458 });
8459 });
8460 view.update(cx, |view, cx| {
8461 view.add_selection_below(&AddSelectionBelow, cx);
8462 assert_eq!(
8463 view.selections.display_ranges(cx),
8464 vec![
8465 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8466 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8467 ]
8468 );
8469 });
8470
8471 view.update(cx, |view, cx| {
8472 view.add_selection_below(&AddSelectionBelow, cx);
8473 assert_eq!(
8474 view.selections.display_ranges(cx),
8475 vec![
8476 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8477 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8478 ]
8479 );
8480 });
8481
8482 view.update(cx, |view, cx| {
8483 view.add_selection_above(&AddSelectionAbove, cx);
8484 assert_eq!(
8485 view.selections.display_ranges(cx),
8486 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8487 );
8488 });
8489
8490 view.update(cx, |view, cx| {
8491 view.add_selection_above(&AddSelectionAbove, cx);
8492 assert_eq!(
8493 view.selections.display_ranges(cx),
8494 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8495 );
8496 });
8497
8498 view.update(cx, |view, cx| {
8499 view.change_selections(None, cx, |s| {
8500 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8501 });
8502 view.add_selection_below(&AddSelectionBelow, cx);
8503 assert_eq!(
8504 view.selections.display_ranges(cx),
8505 vec![
8506 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8507 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8508 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8509 ]
8510 );
8511 });
8512
8513 view.update(cx, |view, cx| {
8514 view.add_selection_below(&AddSelectionBelow, cx);
8515 assert_eq!(
8516 view.selections.display_ranges(cx),
8517 vec![
8518 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8519 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8520 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8521 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8522 ]
8523 );
8524 });
8525
8526 view.update(cx, |view, cx| {
8527 view.add_selection_above(&AddSelectionAbove, cx);
8528 assert_eq!(
8529 view.selections.display_ranges(cx),
8530 vec![
8531 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8532 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8533 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8534 ]
8535 );
8536 });
8537
8538 view.update(cx, |view, cx| {
8539 view.change_selections(None, cx, |s| {
8540 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8541 });
8542 });
8543 view.update(cx, |view, cx| {
8544 view.add_selection_above(&AddSelectionAbove, cx);
8545 assert_eq!(
8546 view.selections.display_ranges(cx),
8547 vec![
8548 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8549 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8550 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8551 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8552 ]
8553 );
8554 });
8555
8556 view.update(cx, |view, cx| {
8557 view.add_selection_below(&AddSelectionBelow, cx);
8558 assert_eq!(
8559 view.selections.display_ranges(cx),
8560 vec![
8561 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8562 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8563 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8564 ]
8565 );
8566 });
8567 }
8568
8569 #[gpui::test]
8570 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8571 cx.set_global(Settings::test(cx));
8572
8573 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8574 let buffer = MultiBuffer::build_simple(&text, cx);
8575 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8576
8577 view.update(cx, |view, cx| {
8578 view.change_selections(None, cx, |s| {
8579 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8580 });
8581 view.select_next(
8582 &SelectNext {
8583 replace_newest: false,
8584 },
8585 cx,
8586 );
8587 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8588
8589 view.select_next(
8590 &SelectNext {
8591 replace_newest: false,
8592 },
8593 cx,
8594 );
8595 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8596
8597 view.undo_selection(&UndoSelection, cx);
8598 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8599
8600 view.redo_selection(&RedoSelection, cx);
8601 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8602
8603 view.select_next(
8604 &SelectNext {
8605 replace_newest: false,
8606 },
8607 cx,
8608 );
8609 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8610
8611 view.select_next(
8612 &SelectNext {
8613 replace_newest: false,
8614 },
8615 cx,
8616 );
8617 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8618 });
8619 }
8620
8621 #[gpui::test]
8622 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8623 cx.update(|cx| cx.set_global(Settings::test(cx)));
8624 let language = Arc::new(Language::new(
8625 LanguageConfig::default(),
8626 Some(tree_sitter_rust::language()),
8627 ));
8628
8629 let text = r#"
8630 use mod1::mod2::{mod3, mod4};
8631
8632 fn fn_1(param1: bool, param2: &str) {
8633 let var1 = "text";
8634 }
8635 "#
8636 .unindent();
8637
8638 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8639 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8640 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8641 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8642 .await;
8643
8644 view.update(cx, |view, cx| {
8645 view.change_selections(None, cx, |s| {
8646 s.select_display_ranges([
8647 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8648 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8649 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8650 ]);
8651 });
8652 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8653 });
8654 assert_eq!(
8655 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8656 &[
8657 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8658 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8659 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8660 ]
8661 );
8662
8663 view.update(cx, |view, cx| {
8664 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8665 });
8666 assert_eq!(
8667 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8668 &[
8669 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8670 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8671 ]
8672 );
8673
8674 view.update(cx, |view, cx| {
8675 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8676 });
8677 assert_eq!(
8678 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8679 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8680 );
8681
8682 // Trying to expand the selected syntax node one more time has no effect.
8683 view.update(cx, |view, cx| {
8684 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8685 });
8686 assert_eq!(
8687 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8688 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8689 );
8690
8691 view.update(cx, |view, cx| {
8692 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8693 });
8694 assert_eq!(
8695 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8696 &[
8697 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8698 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8699 ]
8700 );
8701
8702 view.update(cx, |view, cx| {
8703 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8704 });
8705 assert_eq!(
8706 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8707 &[
8708 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8709 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8710 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8711 ]
8712 );
8713
8714 view.update(cx, |view, cx| {
8715 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8716 });
8717 assert_eq!(
8718 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8719 &[
8720 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8721 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8722 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8723 ]
8724 );
8725
8726 // Trying to shrink the selected syntax node one more time has no effect.
8727 view.update(cx, |view, cx| {
8728 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8729 });
8730 assert_eq!(
8731 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8732 &[
8733 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8734 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8735 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8736 ]
8737 );
8738
8739 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8740 // a fold.
8741 view.update(cx, |view, cx| {
8742 view.fold_ranges(
8743 vec![
8744 Point::new(0, 21)..Point::new(0, 24),
8745 Point::new(3, 20)..Point::new(3, 22),
8746 ],
8747 cx,
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, 16)..DisplayPoint::new(0, 28),
8755 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8756 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8757 ]
8758 );
8759 }
8760
8761 #[gpui::test]
8762 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8763 cx.update(|cx| cx.set_global(Settings::test(cx)));
8764 let language = Arc::new(
8765 Language::new(
8766 LanguageConfig {
8767 brackets: vec![
8768 BracketPair {
8769 start: "{".to_string(),
8770 end: "}".to_string(),
8771 close: false,
8772 newline: true,
8773 },
8774 BracketPair {
8775 start: "(".to_string(),
8776 end: ")".to_string(),
8777 close: false,
8778 newline: true,
8779 },
8780 ],
8781 ..Default::default()
8782 },
8783 Some(tree_sitter_rust::language()),
8784 )
8785 .with_indents_query(
8786 r#"
8787 (_ "(" ")" @end) @indent
8788 (_ "{" "}" @end) @indent
8789 "#,
8790 )
8791 .unwrap(),
8792 );
8793
8794 let text = "fn a() {}";
8795
8796 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8797 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8798 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8799 editor
8800 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8801 .await;
8802
8803 editor.update(cx, |editor, cx| {
8804 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8805 editor.newline(&Newline, cx);
8806 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8807 assert_eq!(
8808 editor.selections.ranges(cx),
8809 &[
8810 Point::new(1, 4)..Point::new(1, 4),
8811 Point::new(3, 4)..Point::new(3, 4),
8812 Point::new(5, 0)..Point::new(5, 0)
8813 ]
8814 );
8815 });
8816 }
8817
8818 #[gpui::test]
8819 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8820 cx.update(|cx| cx.set_global(Settings::test(cx)));
8821 let language = Arc::new(Language::new(
8822 LanguageConfig {
8823 brackets: vec![
8824 BracketPair {
8825 start: "{".to_string(),
8826 end: "}".to_string(),
8827 close: true,
8828 newline: true,
8829 },
8830 BracketPair {
8831 start: "/*".to_string(),
8832 end: " */".to_string(),
8833 close: true,
8834 newline: true,
8835 },
8836 ],
8837 autoclose_before: "})]".to_string(),
8838 ..Default::default()
8839 },
8840 Some(tree_sitter_rust::language()),
8841 ));
8842
8843 let text = r#"
8844 a
8845
8846 /
8847
8848 "#
8849 .unindent();
8850
8851 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8852 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8853 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8854 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8855 .await;
8856
8857 view.update(cx, |view, cx| {
8858 view.change_selections(None, cx, |s| {
8859 s.select_display_ranges([
8860 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8861 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8862 ])
8863 });
8864
8865 view.handle_input(&Input("{".to_string()), cx);
8866 view.handle_input(&Input("{".to_string()), cx);
8867 view.handle_input(&Input("{".to_string()), cx);
8868 assert_eq!(
8869 view.text(cx),
8870 "
8871 {{{}}}
8872 {{{}}}
8873 /
8874
8875 "
8876 .unindent()
8877 );
8878
8879 view.move_right(&MoveRight, cx);
8880 view.handle_input(&Input("}".to_string()), cx);
8881 view.handle_input(&Input("}".to_string()), cx);
8882 view.handle_input(&Input("}".to_string()), cx);
8883 assert_eq!(
8884 view.text(cx),
8885 "
8886 {{{}}}}
8887 {{{}}}}
8888 /
8889
8890 "
8891 .unindent()
8892 );
8893
8894 view.undo(&Undo, cx);
8895 view.handle_input(&Input("/".to_string()), cx);
8896 view.handle_input(&Input("*".to_string()), cx);
8897 assert_eq!(
8898 view.text(cx),
8899 "
8900 /* */
8901 /* */
8902 /
8903
8904 "
8905 .unindent()
8906 );
8907
8908 view.undo(&Undo, cx);
8909 view.change_selections(None, cx, |s| {
8910 s.select_display_ranges([
8911 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8912 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8913 ])
8914 });
8915 view.handle_input(&Input("*".to_string()), cx);
8916 assert_eq!(
8917 view.text(cx),
8918 "
8919 a
8920
8921 /*
8922 *
8923 "
8924 .unindent()
8925 );
8926
8927 // Don't autoclose if the next character isn't whitespace and isn't
8928 // listed in the language's "autoclose_before" section.
8929 view.finalize_last_transaction(cx);
8930 view.change_selections(None, cx, |s| {
8931 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8932 });
8933 view.handle_input(&Input("{".to_string()), cx);
8934 assert_eq!(
8935 view.text(cx),
8936 "
8937 {a
8938
8939 /*
8940 *
8941 "
8942 .unindent()
8943 );
8944
8945 view.undo(&Undo, cx);
8946 view.change_selections(None, cx, |s| {
8947 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8948 });
8949 view.handle_input(&Input("{".to_string()), cx);
8950 assert_eq!(
8951 view.text(cx),
8952 "
8953 {a}
8954
8955 /*
8956 *
8957 "
8958 .unindent()
8959 );
8960 assert_eq!(
8961 view.selections.display_ranges(cx),
8962 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8963 );
8964 });
8965 }
8966
8967 #[gpui::test]
8968 async fn test_snippets(cx: &mut gpui::TestAppContext) {
8969 cx.update(|cx| cx.set_global(Settings::test(cx)));
8970
8971 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
8972 a.| b
8973 a.| b
8974 a.| b"});
8975 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
8976 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8977
8978 editor.update(cx, |editor, cx| {
8979 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
8980
8981 editor
8982 .insert_snippet(&insertion_ranges, snippet, cx)
8983 .unwrap();
8984
8985 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
8986 let range_markers = ('<', '>');
8987 let (expected_text, mut selection_ranges_lookup) =
8988 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
8989 let selection_ranges = selection_ranges_lookup
8990 .remove(&range_markers.into())
8991 .unwrap();
8992 assert_eq!(editor.text(cx), expected_text);
8993 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
8994 }
8995 assert(
8996 editor,
8997 cx,
8998 indoc! {"
8999 a.f(<one>, two, <three>) b
9000 a.f(<one>, two, <three>) b
9001 a.f(<one>, two, <three>) b"},
9002 );
9003
9004 // Can't move earlier than the first tab stop
9005 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9006 assert(
9007 editor,
9008 cx,
9009 indoc! {"
9010 a.f(<one>, two, <three>) b
9011 a.f(<one>, two, <three>) b
9012 a.f(<one>, two, <three>) b"},
9013 );
9014
9015 assert!(editor.move_to_next_snippet_tabstop(cx));
9016 assert(
9017 editor,
9018 cx,
9019 indoc! {"
9020 a.f(one, <two>, three) b
9021 a.f(one, <two>, three) b
9022 a.f(one, <two>, three) b"},
9023 );
9024
9025 editor.move_to_prev_snippet_tabstop(cx);
9026 assert(
9027 editor,
9028 cx,
9029 indoc! {"
9030 a.f(<one>, two, <three>) b
9031 a.f(<one>, two, <three>) b
9032 a.f(<one>, two, <three>) b"},
9033 );
9034
9035 assert!(editor.move_to_next_snippet_tabstop(cx));
9036 assert(
9037 editor,
9038 cx,
9039 indoc! {"
9040 a.f(one, <two>, three) b
9041 a.f(one, <two>, three) b
9042 a.f(one, <two>, three) b"},
9043 );
9044 assert!(editor.move_to_next_snippet_tabstop(cx));
9045 assert(
9046 editor,
9047 cx,
9048 indoc! {"
9049 a.f(one, two, three)<> b
9050 a.f(one, two, three)<> b
9051 a.f(one, two, three)<> b"},
9052 );
9053
9054 // As soon as the last tab stop is reached, snippet state is gone
9055 editor.move_to_prev_snippet_tabstop(cx);
9056 assert(
9057 editor,
9058 cx,
9059 indoc! {"
9060 a.f(one, two, three)<> b
9061 a.f(one, two, three)<> b
9062 a.f(one, two, three)<> b"},
9063 );
9064 });
9065 }
9066
9067 #[gpui::test]
9068 async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
9069 cx.foreground().forbid_parking();
9070 cx.update(|cx| cx.set_global(Settings::test(cx)));
9071
9072 let mut language = Language::new(
9073 LanguageConfig {
9074 name: "Rust".into(),
9075 path_suffixes: vec!["rs".to_string()],
9076 ..Default::default()
9077 },
9078 Some(tree_sitter_rust::language()),
9079 );
9080 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9081 capabilities: lsp::ServerCapabilities {
9082 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9083 ..Default::default()
9084 },
9085 ..Default::default()
9086 });
9087
9088 let fs = FakeFs::new(cx.background().clone());
9089 fs.insert_file("/file.rs", Default::default()).await;
9090
9091 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9092 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9093 let buffer = project
9094 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9095 .await
9096 .unwrap();
9097
9098 cx.foreground().start_waiting();
9099 let fake_server = fake_servers.next().await.unwrap();
9100
9101 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9102 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9103 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9104 assert!(cx.read(|cx| editor.is_dirty(cx)));
9105
9106 let save = cx.update(|cx| editor.save(project.clone(), cx));
9107 fake_server
9108 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9109 assert_eq!(
9110 params.text_document.uri,
9111 lsp::Url::from_file_path("/file.rs").unwrap()
9112 );
9113 assert_eq!(params.options.tab_size, 4);
9114 Ok(Some(vec![lsp::TextEdit::new(
9115 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9116 ", ".to_string(),
9117 )]))
9118 })
9119 .next()
9120 .await;
9121 cx.foreground().start_waiting();
9122 save.await.unwrap();
9123 assert_eq!(
9124 editor.read_with(cx, |editor, cx| editor.text(cx)),
9125 "one, two\nthree\n"
9126 );
9127 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9128
9129 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9130 assert!(cx.read(|cx| editor.is_dirty(cx)));
9131
9132 // Ensure we can still save even if formatting hangs.
9133 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9134 assert_eq!(
9135 params.text_document.uri,
9136 lsp::Url::from_file_path("/file.rs").unwrap()
9137 );
9138 futures::future::pending::<()>().await;
9139 unreachable!()
9140 });
9141 let save = cx.update(|cx| editor.save(project.clone(), cx));
9142 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9143 cx.foreground().start_waiting();
9144 save.await.unwrap();
9145 assert_eq!(
9146 editor.read_with(cx, |editor, cx| editor.text(cx)),
9147 "one\ntwo\nthree\n"
9148 );
9149 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9150
9151 // Set rust language override and assert overriden tabsize is sent to language server
9152 cx.update(|cx| {
9153 cx.update_global::<Settings, _, _>(|settings, _| {
9154 settings.language_overrides.insert(
9155 "Rust".into(),
9156 LanguageOverride {
9157 tab_size: Some(8),
9158 ..Default::default()
9159 },
9160 );
9161 })
9162 });
9163
9164 let save = cx.update(|cx| editor.save(project.clone(), cx));
9165 fake_server
9166 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9167 assert_eq!(
9168 params.text_document.uri,
9169 lsp::Url::from_file_path("/file.rs").unwrap()
9170 );
9171 assert_eq!(params.options.tab_size, 8);
9172 Ok(Some(vec![]))
9173 })
9174 .next()
9175 .await;
9176 cx.foreground().start_waiting();
9177 save.await.unwrap();
9178 }
9179
9180 #[gpui::test]
9181 async fn test_completion(cx: &mut gpui::TestAppContext) {
9182 cx.update(|cx| cx.set_global(Settings::test(cx)));
9183
9184 let mut language = Language::new(
9185 LanguageConfig {
9186 name: "Rust".into(),
9187 path_suffixes: vec!["rs".to_string()],
9188 ..Default::default()
9189 },
9190 Some(tree_sitter_rust::language()),
9191 );
9192 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9193 capabilities: lsp::ServerCapabilities {
9194 completion_provider: Some(lsp::CompletionOptions {
9195 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9196 ..Default::default()
9197 }),
9198 ..Default::default()
9199 },
9200 ..Default::default()
9201 });
9202
9203 let text = "
9204 one
9205 two
9206 three
9207 "
9208 .unindent();
9209
9210 let fs = FakeFs::new(cx.background().clone());
9211 fs.insert_file("/file.rs", text).await;
9212
9213 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9214 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9215 let buffer = project
9216 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9217 .await
9218 .unwrap();
9219 let mut fake_server = fake_servers.next().await.unwrap();
9220
9221 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9222 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9223
9224 editor.update(cx, |editor, cx| {
9225 editor.project = Some(project);
9226 editor.change_selections(None, cx, |s| {
9227 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9228 });
9229 editor.handle_input(&Input(".".to_string()), cx);
9230 });
9231
9232 handle_completion_request(
9233 &mut fake_server,
9234 "/file.rs",
9235 Point::new(0, 4),
9236 vec![
9237 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9238 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9239 ],
9240 )
9241 .await;
9242 editor
9243 .condition(&cx, |editor, _| editor.context_menu_visible())
9244 .await;
9245
9246 let apply_additional_edits = editor.update(cx, |editor, cx| {
9247 editor.move_down(&MoveDown, cx);
9248 let apply_additional_edits = editor
9249 .confirm_completion(&ConfirmCompletion::default(), cx)
9250 .unwrap();
9251 assert_eq!(
9252 editor.text(cx),
9253 "
9254 one.second_completion
9255 two
9256 three
9257 "
9258 .unindent()
9259 );
9260 apply_additional_edits
9261 });
9262
9263 handle_resolve_completion_request(
9264 &mut fake_server,
9265 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9266 )
9267 .await;
9268 apply_additional_edits.await.unwrap();
9269 assert_eq!(
9270 editor.read_with(cx, |editor, cx| editor.text(cx)),
9271 "
9272 one.second_completion
9273 two
9274 three
9275 additional edit
9276 "
9277 .unindent()
9278 );
9279
9280 editor.update(cx, |editor, cx| {
9281 editor.change_selections(None, cx, |s| {
9282 s.select_ranges([
9283 Point::new(1, 3)..Point::new(1, 3),
9284 Point::new(2, 5)..Point::new(2, 5),
9285 ])
9286 });
9287
9288 editor.handle_input(&Input(" ".to_string()), cx);
9289 assert!(editor.context_menu.is_none());
9290 editor.handle_input(&Input("s".to_string()), cx);
9291 assert!(editor.context_menu.is_none());
9292 });
9293
9294 handle_completion_request(
9295 &mut fake_server,
9296 "/file.rs",
9297 Point::new(2, 7),
9298 vec![
9299 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9300 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9301 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9302 ],
9303 )
9304 .await;
9305 editor
9306 .condition(&cx, |editor, _| editor.context_menu_visible())
9307 .await;
9308
9309 editor.update(cx, |editor, cx| {
9310 editor.handle_input(&Input("i".to_string()), cx);
9311 });
9312
9313 handle_completion_request(
9314 &mut fake_server,
9315 "/file.rs",
9316 Point::new(2, 8),
9317 vec![
9318 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9319 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9320 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9321 ],
9322 )
9323 .await;
9324 editor
9325 .condition(&cx, |editor, _| editor.context_menu_visible())
9326 .await;
9327
9328 let apply_additional_edits = editor.update(cx, |editor, cx| {
9329 let apply_additional_edits = editor
9330 .confirm_completion(&ConfirmCompletion::default(), cx)
9331 .unwrap();
9332 assert_eq!(
9333 editor.text(cx),
9334 "
9335 one.second_completion
9336 two sixth_completion
9337 three sixth_completion
9338 additional edit
9339 "
9340 .unindent()
9341 );
9342 apply_additional_edits
9343 });
9344 handle_resolve_completion_request(&mut fake_server, None).await;
9345 apply_additional_edits.await.unwrap();
9346
9347 async fn handle_completion_request(
9348 fake: &mut FakeLanguageServer,
9349 path: &'static str,
9350 position: Point,
9351 completions: Vec<(Range<Point>, &'static str)>,
9352 ) {
9353 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9354 let completions = completions.clone();
9355 async move {
9356 assert_eq!(
9357 params.text_document_position.text_document.uri,
9358 lsp::Url::from_file_path(path).unwrap()
9359 );
9360 assert_eq!(
9361 params.text_document_position.position,
9362 lsp::Position::new(position.row, position.column)
9363 );
9364 Ok(Some(lsp::CompletionResponse::Array(
9365 completions
9366 .iter()
9367 .map(|(range, new_text)| lsp::CompletionItem {
9368 label: new_text.to_string(),
9369 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9370 range: lsp::Range::new(
9371 lsp::Position::new(range.start.row, range.start.column),
9372 lsp::Position::new(range.start.row, range.start.column),
9373 ),
9374 new_text: new_text.to_string(),
9375 })),
9376 ..Default::default()
9377 })
9378 .collect(),
9379 )))
9380 }
9381 })
9382 .next()
9383 .await;
9384 }
9385
9386 async fn handle_resolve_completion_request(
9387 fake: &mut FakeLanguageServer,
9388 edit: Option<(Range<Point>, &'static str)>,
9389 ) {
9390 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9391 let edit = edit.clone();
9392 async move {
9393 Ok(lsp::CompletionItem {
9394 additional_text_edits: edit.map(|(range, new_text)| {
9395 vec![lsp::TextEdit::new(
9396 lsp::Range::new(
9397 lsp::Position::new(range.start.row, range.start.column),
9398 lsp::Position::new(range.end.row, range.end.column),
9399 ),
9400 new_text.to_string(),
9401 )]
9402 }),
9403 ..Default::default()
9404 })
9405 }
9406 })
9407 .next()
9408 .await;
9409 }
9410 }
9411
9412 #[gpui::test]
9413 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9414 cx.update(|cx| cx.set_global(Settings::test(cx)));
9415 let language = Arc::new(Language::new(
9416 LanguageConfig {
9417 line_comment: Some("// ".to_string()),
9418 ..Default::default()
9419 },
9420 Some(tree_sitter_rust::language()),
9421 ));
9422
9423 let text = "
9424 fn a() {
9425 //b();
9426 // c();
9427 // d();
9428 }
9429 "
9430 .unindent();
9431
9432 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9433 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9434 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9435
9436 view.update(cx, |editor, cx| {
9437 // If multiple selections intersect a line, the line is only
9438 // toggled once.
9439 editor.change_selections(None, cx, |s| {
9440 s.select_display_ranges([
9441 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9442 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9443 ])
9444 });
9445 editor.toggle_comments(&ToggleComments, cx);
9446 assert_eq!(
9447 editor.text(cx),
9448 "
9449 fn a() {
9450 b();
9451 c();
9452 d();
9453 }
9454 "
9455 .unindent()
9456 );
9457
9458 // The comment prefix is inserted at the same column for every line
9459 // in a selection.
9460 editor.change_selections(None, cx, |s| {
9461 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9462 });
9463 editor.toggle_comments(&ToggleComments, cx);
9464 assert_eq!(
9465 editor.text(cx),
9466 "
9467 fn a() {
9468 // b();
9469 // c();
9470 // d();
9471 }
9472 "
9473 .unindent()
9474 );
9475
9476 // If a selection ends at the beginning of a line, that line is not toggled.
9477 editor.change_selections(None, cx, |s| {
9478 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9479 });
9480 editor.toggle_comments(&ToggleComments, cx);
9481 assert_eq!(
9482 editor.text(cx),
9483 "
9484 fn a() {
9485 // b();
9486 c();
9487 // d();
9488 }
9489 "
9490 .unindent()
9491 );
9492 });
9493 }
9494
9495 #[gpui::test]
9496 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9497 cx.set_global(Settings::test(cx));
9498 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9499 let multibuffer = cx.add_model(|cx| {
9500 let mut multibuffer = MultiBuffer::new(0);
9501 multibuffer.push_excerpts(
9502 buffer.clone(),
9503 [
9504 Point::new(0, 0)..Point::new(0, 4),
9505 Point::new(1, 0)..Point::new(1, 4),
9506 ],
9507 cx,
9508 );
9509 multibuffer
9510 });
9511
9512 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9513
9514 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9515 view.update(cx, |view, cx| {
9516 assert_eq!(view.text(cx), "aaaa\nbbbb");
9517 view.change_selections(None, cx, |s| {
9518 s.select_ranges([
9519 Point::new(0, 0)..Point::new(0, 0),
9520 Point::new(1, 0)..Point::new(1, 0),
9521 ])
9522 });
9523
9524 view.handle_input(&Input("X".to_string()), cx);
9525 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9526 assert_eq!(
9527 view.selections.ranges(cx),
9528 [
9529 Point::new(0, 1)..Point::new(0, 1),
9530 Point::new(1, 1)..Point::new(1, 1),
9531 ]
9532 )
9533 });
9534 }
9535
9536 #[gpui::test]
9537 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9538 cx.set_global(Settings::test(cx));
9539 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9540 [aaaa
9541 (bbbb]
9542 cccc)"});
9543 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9544 let multibuffer = cx.add_model(|cx| {
9545 let mut multibuffer = MultiBuffer::new(0);
9546 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9547 multibuffer
9548 });
9549
9550 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9551 view.update(cx, |view, cx| {
9552 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9553 aaaa
9554 b|bbb
9555 b|bb|b
9556 cccc"});
9557 assert_eq!(view.text(cx), expected_text);
9558 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9559
9560 view.handle_input(&Input("X".to_string()), cx);
9561
9562 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9563 aaaa
9564 bX|bbXb
9565 bX|bbX|b
9566 cccc"});
9567 assert_eq!(view.text(cx), expected_text);
9568 assert_eq!(view.selections.ranges(cx), expected_selections);
9569
9570 view.newline(&Newline, cx);
9571 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9572 aaaa
9573 bX
9574 |bbX
9575 b
9576 bX
9577 |bbX
9578 |b
9579 cccc"});
9580 assert_eq!(view.text(cx), expected_text);
9581 assert_eq!(view.selections.ranges(cx), expected_selections);
9582 });
9583 }
9584
9585 #[gpui::test]
9586 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9587 cx.set_global(Settings::test(cx));
9588 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9589 let mut excerpt1_id = None;
9590 let multibuffer = cx.add_model(|cx| {
9591 let mut multibuffer = MultiBuffer::new(0);
9592 excerpt1_id = multibuffer
9593 .push_excerpts(
9594 buffer.clone(),
9595 [
9596 Point::new(0, 0)..Point::new(1, 4),
9597 Point::new(1, 0)..Point::new(2, 4),
9598 ],
9599 cx,
9600 )
9601 .into_iter()
9602 .next();
9603 multibuffer
9604 });
9605 assert_eq!(
9606 multibuffer.read(cx).read(cx).text(),
9607 "aaaa\nbbbb\nbbbb\ncccc"
9608 );
9609 let (_, editor) = cx.add_window(Default::default(), |cx| {
9610 let mut editor = build_editor(multibuffer.clone(), cx);
9611 let snapshot = editor.snapshot(cx);
9612 editor.change_selections(None, cx, |s| {
9613 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9614 });
9615 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9616 assert_eq!(
9617 editor.selections.ranges(cx),
9618 [
9619 Point::new(1, 3)..Point::new(1, 3),
9620 Point::new(2, 1)..Point::new(2, 1),
9621 ]
9622 );
9623 editor
9624 });
9625
9626 // Refreshing selections is a no-op when excerpts haven't changed.
9627 editor.update(cx, |editor, cx| {
9628 editor.change_selections(None, cx, |s| {
9629 s.refresh();
9630 });
9631 assert_eq!(
9632 editor.selections.ranges(cx),
9633 [
9634 Point::new(1, 3)..Point::new(1, 3),
9635 Point::new(2, 1)..Point::new(2, 1),
9636 ]
9637 );
9638 });
9639
9640 multibuffer.update(cx, |multibuffer, cx| {
9641 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9642 });
9643 editor.update(cx, |editor, cx| {
9644 // Removing an excerpt causes the first selection to become degenerate.
9645 assert_eq!(
9646 editor.selections.ranges(cx),
9647 [
9648 Point::new(0, 0)..Point::new(0, 0),
9649 Point::new(0, 1)..Point::new(0, 1)
9650 ]
9651 );
9652
9653 // Refreshing selections will relocate the first selection to the original buffer
9654 // location.
9655 editor.change_selections(None, cx, |s| {
9656 s.refresh();
9657 });
9658 assert_eq!(
9659 editor.selections.ranges(cx),
9660 [
9661 Point::new(0, 1)..Point::new(0, 1),
9662 Point::new(0, 3)..Point::new(0, 3)
9663 ]
9664 );
9665 assert!(editor.selections.pending_anchor().is_some());
9666 });
9667 }
9668
9669 #[gpui::test]
9670 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9671 cx.set_global(Settings::test(cx));
9672 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9673 let mut excerpt1_id = None;
9674 let multibuffer = cx.add_model(|cx| {
9675 let mut multibuffer = MultiBuffer::new(0);
9676 excerpt1_id = multibuffer
9677 .push_excerpts(
9678 buffer.clone(),
9679 [
9680 Point::new(0, 0)..Point::new(1, 4),
9681 Point::new(1, 0)..Point::new(2, 4),
9682 ],
9683 cx,
9684 )
9685 .into_iter()
9686 .next();
9687 multibuffer
9688 });
9689 assert_eq!(
9690 multibuffer.read(cx).read(cx).text(),
9691 "aaaa\nbbbb\nbbbb\ncccc"
9692 );
9693 let (_, editor) = cx.add_window(Default::default(), |cx| {
9694 let mut editor = build_editor(multibuffer.clone(), cx);
9695 let snapshot = editor.snapshot(cx);
9696 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9697 assert_eq!(
9698 editor.selections.ranges(cx),
9699 [Point::new(1, 3)..Point::new(1, 3)]
9700 );
9701 editor
9702 });
9703
9704 multibuffer.update(cx, |multibuffer, cx| {
9705 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9706 });
9707 editor.update(cx, |editor, cx| {
9708 assert_eq!(
9709 editor.selections.ranges(cx),
9710 [Point::new(0, 0)..Point::new(0, 0)]
9711 );
9712
9713 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9714 editor.change_selections(None, cx, |s| {
9715 s.refresh();
9716 });
9717 assert_eq!(
9718 editor.selections.ranges(cx),
9719 [Point::new(0, 3)..Point::new(0, 3)]
9720 );
9721 assert!(editor.selections.pending_anchor().is_some());
9722 });
9723 }
9724
9725 #[gpui::test]
9726 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9727 cx.update(|cx| cx.set_global(Settings::test(cx)));
9728 let language = Arc::new(Language::new(
9729 LanguageConfig {
9730 brackets: vec![
9731 BracketPair {
9732 start: "{".to_string(),
9733 end: "}".to_string(),
9734 close: true,
9735 newline: true,
9736 },
9737 BracketPair {
9738 start: "/* ".to_string(),
9739 end: " */".to_string(),
9740 close: true,
9741 newline: true,
9742 },
9743 ],
9744 ..Default::default()
9745 },
9746 Some(tree_sitter_rust::language()),
9747 ));
9748
9749 let text = concat!(
9750 "{ }\n", // Suppress rustfmt
9751 " x\n", //
9752 " /* */\n", //
9753 "x\n", //
9754 "{{} }\n", //
9755 );
9756
9757 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9758 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9759 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9760 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9761 .await;
9762
9763 view.update(cx, |view, cx| {
9764 view.change_selections(None, cx, |s| {
9765 s.select_display_ranges([
9766 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9767 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9768 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9769 ])
9770 });
9771 view.newline(&Newline, cx);
9772
9773 assert_eq!(
9774 view.buffer().read(cx).read(cx).text(),
9775 concat!(
9776 "{ \n", // Suppress rustfmt
9777 "\n", //
9778 "}\n", //
9779 " x\n", //
9780 " /* \n", //
9781 " \n", //
9782 " */\n", //
9783 "x\n", //
9784 "{{} \n", //
9785 "}\n", //
9786 )
9787 );
9788 });
9789 }
9790
9791 #[gpui::test]
9792 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9793 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9794
9795 cx.set_global(Settings::test(cx));
9796 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9797
9798 editor.update(cx, |editor, cx| {
9799 struct Type1;
9800 struct Type2;
9801
9802 let buffer = buffer.read(cx).snapshot(cx);
9803
9804 let anchor_range = |range: Range<Point>| {
9805 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9806 };
9807
9808 editor.highlight_background::<Type1>(
9809 vec![
9810 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
9811 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
9812 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
9813 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
9814 ],
9815 |_| Color::red(),
9816 cx,
9817 );
9818 editor.highlight_background::<Type2>(
9819 vec![
9820 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
9821 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
9822 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
9823 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
9824 ],
9825 |_| Color::green(),
9826 cx,
9827 );
9828
9829 let snapshot = editor.snapshot(cx);
9830 let mut highlighted_ranges = editor.background_highlights_in_range(
9831 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
9832 &snapshot,
9833 cx.global::<Settings>().theme.as_ref(),
9834 );
9835 // Enforce a consistent ordering based on color without relying on the ordering of the
9836 // highlight's `TypeId` which is non-deterministic.
9837 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
9838 assert_eq!(
9839 highlighted_ranges,
9840 &[
9841 (
9842 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
9843 Color::green(),
9844 ),
9845 (
9846 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
9847 Color::green(),
9848 ),
9849 (
9850 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
9851 Color::red(),
9852 ),
9853 (
9854 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9855 Color::red(),
9856 ),
9857 ]
9858 );
9859 assert_eq!(
9860 editor.background_highlights_in_range(
9861 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
9862 &snapshot,
9863 cx.global::<Settings>().theme.as_ref(),
9864 ),
9865 &[(
9866 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9867 Color::red(),
9868 )]
9869 );
9870 });
9871 }
9872
9873 #[gpui::test]
9874 fn test_following(cx: &mut gpui::MutableAppContext) {
9875 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9876
9877 cx.set_global(Settings::test(cx));
9878
9879 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9880 let (_, follower) = cx.add_window(
9881 WindowOptions {
9882 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
9883 ..Default::default()
9884 },
9885 |cx| build_editor(buffer.clone(), cx),
9886 );
9887
9888 let pending_update = Rc::new(RefCell::new(None));
9889 follower.update(cx, {
9890 let update = pending_update.clone();
9891 |_, cx| {
9892 cx.subscribe(&leader, move |_, leader, event, cx| {
9893 leader
9894 .read(cx)
9895 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
9896 })
9897 .detach();
9898 }
9899 });
9900
9901 // Update the selections only
9902 leader.update(cx, |leader, cx| {
9903 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9904 });
9905 follower.update(cx, |follower, cx| {
9906 follower
9907 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9908 .unwrap();
9909 });
9910 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
9911
9912 // Update the scroll position only
9913 leader.update(cx, |leader, cx| {
9914 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9915 });
9916 follower.update(cx, |follower, cx| {
9917 follower
9918 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9919 .unwrap();
9920 });
9921 assert_eq!(
9922 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
9923 vec2f(1.5, 3.5)
9924 );
9925
9926 // Update the selections and scroll position
9927 leader.update(cx, |leader, cx| {
9928 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
9929 leader.request_autoscroll(Autoscroll::Newest, cx);
9930 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9931 });
9932 follower.update(cx, |follower, cx| {
9933 let initial_scroll_position = follower.scroll_position(cx);
9934 follower
9935 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9936 .unwrap();
9937 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
9938 assert!(follower.autoscroll_request.is_some());
9939 });
9940 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
9941
9942 // Creating a pending selection that precedes another selection
9943 leader.update(cx, |leader, cx| {
9944 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9945 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
9946 });
9947 follower.update(cx, |follower, cx| {
9948 follower
9949 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9950 .unwrap();
9951 });
9952 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
9953
9954 // Extend the pending selection so that it surrounds another selection
9955 leader.update(cx, |leader, cx| {
9956 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
9957 });
9958 follower.update(cx, |follower, cx| {
9959 follower
9960 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9961 .unwrap();
9962 });
9963 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
9964 }
9965
9966 #[test]
9967 fn test_combine_syntax_and_fuzzy_match_highlights() {
9968 let string = "abcdefghijklmnop";
9969 let syntax_ranges = [
9970 (
9971 0..3,
9972 HighlightStyle {
9973 color: Some(Color::red()),
9974 ..Default::default()
9975 },
9976 ),
9977 (
9978 4..8,
9979 HighlightStyle {
9980 color: Some(Color::green()),
9981 ..Default::default()
9982 },
9983 ),
9984 ];
9985 let match_indices = [4, 6, 7, 8];
9986 assert_eq!(
9987 combine_syntax_and_fuzzy_match_highlights(
9988 &string,
9989 Default::default(),
9990 syntax_ranges.into_iter(),
9991 &match_indices,
9992 ),
9993 &[
9994 (
9995 0..3,
9996 HighlightStyle {
9997 color: Some(Color::red()),
9998 ..Default::default()
9999 },
10000 ),
10001 (
10002 4..5,
10003 HighlightStyle {
10004 color: Some(Color::green()),
10005 weight: Some(fonts::Weight::BOLD),
10006 ..Default::default()
10007 },
10008 ),
10009 (
10010 5..6,
10011 HighlightStyle {
10012 color: Some(Color::green()),
10013 ..Default::default()
10014 },
10015 ),
10016 (
10017 6..8,
10018 HighlightStyle {
10019 color: Some(Color::green()),
10020 weight: Some(fonts::Weight::BOLD),
10021 ..Default::default()
10022 },
10023 ),
10024 (
10025 8..9,
10026 HighlightStyle {
10027 weight: Some(fonts::Weight::BOLD),
10028 ..Default::default()
10029 },
10030 ),
10031 ]
10032 );
10033 }
10034
10035 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10036 let point = DisplayPoint::new(row as u32, column as u32);
10037 point..point
10038 }
10039
10040 fn assert_selection_ranges(
10041 marked_text: &str,
10042 selection_marker_pairs: Vec<(char, char)>,
10043 view: &mut Editor,
10044 cx: &mut ViewContext<Editor>,
10045 ) {
10046 let snapshot = view.snapshot(cx).display_snapshot;
10047 let mut marker_chars = Vec::new();
10048 for (start, end) in selection_marker_pairs.iter() {
10049 marker_chars.push(*start);
10050 marker_chars.push(*end);
10051 }
10052 let (_, markers) = marked_text_by(marked_text, marker_chars);
10053 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10054 .iter()
10055 .map(|(start, end)| {
10056 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10057 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10058 start..end
10059 })
10060 .collect();
10061 assert_eq!(
10062 view.selections.display_ranges(cx),
10063 &asserted_ranges[..],
10064 "Assert selections are {}",
10065 marked_text
10066 );
10067 }
10068}
10069
10070trait RangeExt<T> {
10071 fn sorted(&self) -> Range<T>;
10072 fn to_inclusive(&self) -> RangeInclusive<T>;
10073}
10074
10075impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10076 fn sorted(&self) -> Self {
10077 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10078 }
10079
10080 fn to_inclusive(&self) -> RangeInclusive<T> {
10081 self.start.clone()..=self.end.clone()
10082 }
10083}