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