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