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