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