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