1pub mod display_map;
2mod element;
3mod highlight_matching_bracket;
4mod hover_popover;
5pub mod items;
6mod link_go_to_definition;
7mod mouse_context_menu;
8pub mod movement;
9mod multi_buffer;
10pub mod selections_collection;
11
12#[cfg(any(test, feature = "test-support"))]
13pub mod test;
14
15use aho_corasick::AhoCorasick;
16use anyhow::Result;
17use clock::ReplicaId;
18use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
19pub use display_map::DisplayPoint;
20use display_map::*;
21pub use element::*;
22use fuzzy::{StringMatch, StringMatchCandidate};
23use gpui::{
24 actions,
25 color::Color,
26 elements::*,
27 executor,
28 fonts::{self, HighlightStyle, TextStyle},
29 geometry::vector::{vec2f, Vector2F},
30 impl_actions, impl_internal_actions,
31 platform::CursorStyle,
32 text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
33 ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
34 ViewContext, ViewHandle, WeakViewHandle,
35};
36use highlight_matching_bracket::refresh_matching_bracket_highlights;
37use hover_popover::{hide_hover, HoverState};
38pub use items::MAX_TAB_TITLE_LEN;
39pub use language::{char_kind, CharKind};
40use language::{
41 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
42 DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point,
43 Selection, SelectionGoal, TransactionId,
44};
45use link_go_to_definition::LinkGoToDefinitionState;
46pub use multi_buffer::{
47 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
48 ToPoint,
49};
50use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
51use ordered_float::OrderedFloat;
52use project::{LocationLink, Project, ProjectPath, ProjectTransaction};
53use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
54use serde::{Deserialize, Serialize};
55use settings::Settings;
56use smallvec::SmallVec;
57use smol::Timer;
58use snippet::Snippet;
59use std::{
60 any::TypeId,
61 borrow::Cow,
62 cmp::{self, Ordering, Reverse},
63 mem,
64 num::NonZeroU32,
65 ops::{Deref, DerefMut, Range, RangeInclusive},
66 sync::Arc,
67 time::{Duration, Instant},
68};
69pub use sum_tree::Bias;
70use theme::{DiagnosticStyle, Theme};
71use util::{post_inc, ResultExt, TryFutureExt};
72use workspace::{ItemNavHistory, Workspace};
73
74const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
75const MAX_LINE_LEN: usize = 1024;
76const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
77const MAX_SELECTION_HISTORY_LEN: usize = 1024;
78
79#[derive(Clone, Deserialize, PartialEq, Default)]
80pub struct SelectNext {
81 #[serde(default)]
82 pub replace_newest: bool,
83}
84
85#[derive(Clone, PartialEq)]
86pub struct Scroll(pub Vector2F);
87
88#[derive(Clone, PartialEq)]
89pub struct Select(pub SelectPhase);
90
91#[derive(Clone, Debug, PartialEq)]
92pub struct Jump {
93 path: ProjectPath,
94 position: Point,
95 anchor: language::Anchor,
96}
97
98#[derive(Clone, Deserialize, PartialEq)]
99pub struct SelectToBeginningOfLine {
100 #[serde(default)]
101 stop_at_soft_wraps: bool,
102}
103
104#[derive(Clone, Deserialize, PartialEq)]
105pub struct SelectToEndOfLine {
106 #[serde(default)]
107 stop_at_soft_wraps: bool,
108}
109
110#[derive(Clone, Deserialize, PartialEq)]
111pub struct ToggleCodeActions {
112 #[serde(default)]
113 pub deployed_from_indicator: bool,
114}
115
116#[derive(Clone, Default, Deserialize, PartialEq)]
117pub struct ConfirmCompletion {
118 #[serde(default)]
119 pub item_ix: Option<usize>,
120}
121
122#[derive(Clone, Default, Deserialize, PartialEq)]
123pub struct ConfirmCodeAction {
124 #[serde(default)]
125 pub item_ix: Option<usize>,
126}
127
128actions!(
129 editor,
130 [
131 Cancel,
132 Backspace,
133 Delete,
134 Newline,
135 NewlineBelow,
136 GoToDiagnostic,
137 GoToPrevDiagnostic,
138 Indent,
139 Outdent,
140 DeleteLine,
141 DeleteToPreviousWordStart,
142 DeleteToPreviousSubwordStart,
143 DeleteToNextWordEnd,
144 DeleteToNextSubwordEnd,
145 DeleteToBeginningOfLine,
146 DeleteToEndOfLine,
147 CutToEndOfLine,
148 DuplicateLine,
149 MoveLineUp,
150 MoveLineDown,
151 Transpose,
152 Cut,
153 Copy,
154 Paste,
155 Undo,
156 Redo,
157 MoveUp,
158 MoveDown,
159 MoveLeft,
160 MoveRight,
161 MoveToPreviousWordStart,
162 MoveToPreviousSubwordStart,
163 MoveToNextWordEnd,
164 MoveToNextSubwordEnd,
165 MoveToBeginningOfLine,
166 MoveToEndOfLine,
167 MoveToBeginning,
168 MoveToEnd,
169 SelectUp,
170 SelectDown,
171 SelectLeft,
172 SelectRight,
173 SelectToPreviousWordStart,
174 SelectToPreviousSubwordStart,
175 SelectToNextWordEnd,
176 SelectToNextSubwordEnd,
177 SelectToBeginning,
178 SelectToEnd,
179 SelectAll,
180 SelectLine,
181 SplitSelectionIntoLines,
182 AddSelectionAbove,
183 AddSelectionBelow,
184 Tab,
185 TabPrev,
186 ToggleComments,
187 ShowCharacterPalette,
188 SelectLargerSyntaxNode,
189 SelectSmallerSyntaxNode,
190 GoToDefinition,
191 GoToTypeDefinition,
192 MoveToEnclosingBracket,
193 UndoSelection,
194 RedoSelection,
195 FindAllReferences,
196 Rename,
197 ConfirmRename,
198 PageUp,
199 PageDown,
200 Fold,
201 UnfoldLines,
202 FoldSelectedRanges,
203 ShowCompletions,
204 OpenExcerpts,
205 RestartLanguageServer,
206 Hover,
207 ]
208);
209
210impl_actions!(
211 editor,
212 [
213 SelectNext,
214 SelectToBeginningOfLine,
215 SelectToEndOfLine,
216 ToggleCodeActions,
217 ConfirmCompletion,
218 ConfirmCodeAction,
219 ]
220);
221
222impl_internal_actions!(editor, [Scroll, Select, Jump]);
223
224enum DocumentHighlightRead {}
225enum DocumentHighlightWrite {}
226enum InputComposition {}
227
228#[derive(Copy, Clone, PartialEq, Eq)]
229pub enum Direction {
230 Prev,
231 Next,
232}
233
234pub fn init(cx: &mut MutableAppContext) {
235 cx.add_action(Editor::new_file);
236 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
237 cx.add_action(Editor::select);
238 cx.add_action(Editor::cancel);
239 cx.add_action(Editor::newline);
240 cx.add_action(Editor::newline_below);
241 cx.add_action(Editor::backspace);
242 cx.add_action(Editor::delete);
243 cx.add_action(Editor::tab);
244 cx.add_action(Editor::tab_prev);
245 cx.add_action(Editor::indent);
246 cx.add_action(Editor::outdent);
247 cx.add_action(Editor::delete_line);
248 cx.add_action(Editor::delete_to_previous_word_start);
249 cx.add_action(Editor::delete_to_previous_subword_start);
250 cx.add_action(Editor::delete_to_next_word_end);
251 cx.add_action(Editor::delete_to_next_subword_end);
252 cx.add_action(Editor::delete_to_beginning_of_line);
253 cx.add_action(Editor::delete_to_end_of_line);
254 cx.add_action(Editor::cut_to_end_of_line);
255 cx.add_action(Editor::duplicate_line);
256 cx.add_action(Editor::move_line_up);
257 cx.add_action(Editor::move_line_down);
258 cx.add_action(Editor::transpose);
259 cx.add_action(Editor::cut);
260 cx.add_action(Editor::copy);
261 cx.add_action(Editor::paste);
262 cx.add_action(Editor::undo);
263 cx.add_action(Editor::redo);
264 cx.add_action(Editor::move_up);
265 cx.add_action(Editor::move_down);
266 cx.add_action(Editor::move_left);
267 cx.add_action(Editor::move_right);
268 cx.add_action(Editor::move_to_previous_word_start);
269 cx.add_action(Editor::move_to_previous_subword_start);
270 cx.add_action(Editor::move_to_next_word_end);
271 cx.add_action(Editor::move_to_next_subword_end);
272 cx.add_action(Editor::move_to_beginning_of_line);
273 cx.add_action(Editor::move_to_end_of_line);
274 cx.add_action(Editor::move_to_beginning);
275 cx.add_action(Editor::move_to_end);
276 cx.add_action(Editor::select_up);
277 cx.add_action(Editor::select_down);
278 cx.add_action(Editor::select_left);
279 cx.add_action(Editor::select_right);
280 cx.add_action(Editor::select_to_previous_word_start);
281 cx.add_action(Editor::select_to_previous_subword_start);
282 cx.add_action(Editor::select_to_next_word_end);
283 cx.add_action(Editor::select_to_next_subword_end);
284 cx.add_action(Editor::select_to_beginning_of_line);
285 cx.add_action(Editor::select_to_end_of_line);
286 cx.add_action(Editor::select_to_beginning);
287 cx.add_action(Editor::select_to_end);
288 cx.add_action(Editor::select_all);
289 cx.add_action(Editor::select_line);
290 cx.add_action(Editor::split_selection_into_lines);
291 cx.add_action(Editor::add_selection_above);
292 cx.add_action(Editor::add_selection_below);
293 cx.add_action(Editor::select_next);
294 cx.add_action(Editor::toggle_comments);
295 cx.add_action(Editor::select_larger_syntax_node);
296 cx.add_action(Editor::select_smaller_syntax_node);
297 cx.add_action(Editor::move_to_enclosing_bracket);
298 cx.add_action(Editor::undo_selection);
299 cx.add_action(Editor::redo_selection);
300 cx.add_action(Editor::go_to_diagnostic);
301 cx.add_action(Editor::go_to_prev_diagnostic);
302 cx.add_action(Editor::go_to_definition);
303 cx.add_action(Editor::go_to_type_definition);
304 cx.add_action(Editor::page_up);
305 cx.add_action(Editor::page_down);
306 cx.add_action(Editor::fold);
307 cx.add_action(Editor::unfold_lines);
308 cx.add_action(Editor::fold_selected_ranges);
309 cx.add_action(Editor::show_completions);
310 cx.add_action(Editor::toggle_code_actions);
311 cx.add_action(Editor::open_excerpts);
312 cx.add_action(Editor::jump);
313 cx.add_action(Editor::restart_language_server);
314 cx.add_action(Editor::show_character_palette);
315 cx.add_async_action(Editor::confirm_completion);
316 cx.add_async_action(Editor::confirm_code_action);
317 cx.add_async_action(Editor::rename);
318 cx.add_async_action(Editor::confirm_rename);
319 cx.add_async_action(Editor::find_all_references);
320
321 hover_popover::init(cx);
322 link_go_to_definition::init(cx);
323 mouse_context_menu::init(cx);
324
325 workspace::register_project_item::<Editor>(cx);
326 workspace::register_followable_item::<Editor>(cx);
327}
328
329trait InvalidationRegion {
330 fn ranges(&self) -> &[Range<Anchor>];
331}
332
333#[derive(Clone, Debug, PartialEq)]
334pub enum SelectPhase {
335 Begin {
336 position: DisplayPoint,
337 add: bool,
338 click_count: usize,
339 },
340 BeginColumnar {
341 position: DisplayPoint,
342 goal_column: u32,
343 },
344 Extend {
345 position: DisplayPoint,
346 click_count: usize,
347 },
348 Update {
349 position: DisplayPoint,
350 goal_column: u32,
351 scroll_position: Vector2F,
352 },
353 End,
354}
355
356#[derive(Clone, Debug)]
357pub enum SelectMode {
358 Character,
359 Word(Range<Anchor>),
360 Line(Range<Anchor>),
361 All,
362}
363
364#[derive(PartialEq, Eq)]
365pub enum Autoscroll {
366 Fit,
367 Center,
368 Newest,
369}
370
371#[derive(Copy, Clone, PartialEq, Eq)]
372pub enum EditorMode {
373 SingleLine,
374 AutoHeight { max_lines: usize },
375 Full,
376}
377
378#[derive(Clone)]
379pub enum SoftWrap {
380 None,
381 EditorWidth,
382 Column(u32),
383}
384
385#[derive(Clone)]
386pub struct EditorStyle {
387 pub text: TextStyle,
388 pub placeholder_text: Option<TextStyle>,
389 pub theme: theme::Editor,
390}
391
392type CompletionId = usize;
393
394pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
395
396type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
397
398pub struct Editor {
399 handle: WeakViewHandle<Self>,
400 buffer: ModelHandle<MultiBuffer>,
401 display_map: ModelHandle<DisplayMap>,
402 pub selections: SelectionsCollection,
403 columnar_selection_tail: Option<Anchor>,
404 add_selections_state: Option<AddSelectionsState>,
405 select_next_state: Option<SelectNextState>,
406 selection_history: SelectionHistory,
407 autoclose_stack: InvalidationStack<BracketPairState>,
408 snippet_stack: InvalidationStack<SnippetState>,
409 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
410 ime_transaction: Option<TransactionId>,
411 active_diagnostics: Option<ActiveDiagnosticGroup>,
412 scroll_position: Vector2F,
413 scroll_top_anchor: Anchor,
414 autoscroll_request: Option<(Autoscroll, bool)>,
415 soft_wrap_mode_override: Option<settings::SoftWrap>,
416 get_field_editor_theme: Option<GetFieldEditorTheme>,
417 override_text_style: Option<Box<OverrideTextStyle>>,
418 project: Option<ModelHandle<Project>>,
419 focused: bool,
420 show_local_cursors: bool,
421 show_local_selections: bool,
422 blink_epoch: usize,
423 blinking_paused: bool,
424 mode: EditorMode,
425 vertical_scroll_margin: f32,
426 placeholder_text: Option<Arc<str>>,
427 highlighted_rows: Option<Range<u32>>,
428 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
429 nav_history: Option<ItemNavHistory>,
430 context_menu: Option<ContextMenu>,
431 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
432 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
433 next_completion_id: CompletionId,
434 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
435 code_actions_task: Option<Task<()>>,
436 document_highlights_task: Option<Task<()>>,
437 pending_rename: Option<RenameState>,
438 searchable: bool,
439 cursor_shape: CursorShape,
440 keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
441 input_enabled: bool,
442 leader_replica_id: Option<u16>,
443 hover_state: HoverState,
444 link_go_to_definition_state: LinkGoToDefinitionState,
445 _subscriptions: Vec<Subscription>,
446}
447
448pub struct EditorSnapshot {
449 pub mode: EditorMode,
450 pub display_snapshot: DisplaySnapshot,
451 pub placeholder_text: Option<Arc<str>>,
452 is_focused: bool,
453 scroll_position: Vector2F,
454 scroll_top_anchor: Anchor,
455}
456
457#[derive(Clone, Debug)]
458struct SelectionHistoryEntry {
459 selections: Arc<[Selection<Anchor>]>,
460 select_next_state: Option<SelectNextState>,
461 add_selections_state: Option<AddSelectionsState>,
462}
463
464enum SelectionHistoryMode {
465 Normal,
466 Undoing,
467 Redoing,
468}
469
470impl Default for SelectionHistoryMode {
471 fn default() -> Self {
472 Self::Normal
473 }
474}
475
476#[derive(Default)]
477struct SelectionHistory {
478 selections_by_transaction:
479 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
480 mode: SelectionHistoryMode,
481 undo_stack: VecDeque<SelectionHistoryEntry>,
482 redo_stack: VecDeque<SelectionHistoryEntry>,
483}
484
485impl SelectionHistory {
486 fn insert_transaction(
487 &mut self,
488 transaction_id: TransactionId,
489 selections: Arc<[Selection<Anchor>]>,
490 ) {
491 self.selections_by_transaction
492 .insert(transaction_id, (selections, None));
493 }
494
495 fn transaction(
496 &self,
497 transaction_id: TransactionId,
498 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
499 self.selections_by_transaction.get(&transaction_id)
500 }
501
502 fn transaction_mut(
503 &mut self,
504 transaction_id: TransactionId,
505 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
506 self.selections_by_transaction.get_mut(&transaction_id)
507 }
508
509 fn push(&mut self, entry: SelectionHistoryEntry) {
510 if !entry.selections.is_empty() {
511 match self.mode {
512 SelectionHistoryMode::Normal => {
513 self.push_undo(entry);
514 self.redo_stack.clear();
515 }
516 SelectionHistoryMode::Undoing => self.push_redo(entry),
517 SelectionHistoryMode::Redoing => self.push_undo(entry),
518 }
519 }
520 }
521
522 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
523 if self
524 .undo_stack
525 .back()
526 .map_or(true, |e| e.selections != entry.selections)
527 {
528 self.undo_stack.push_back(entry);
529 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
530 self.undo_stack.pop_front();
531 }
532 }
533 }
534
535 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
536 if self
537 .redo_stack
538 .back()
539 .map_or(true, |e| e.selections != entry.selections)
540 {
541 self.redo_stack.push_back(entry);
542 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
543 self.redo_stack.pop_front();
544 }
545 }
546 }
547}
548
549#[derive(Clone, Debug)]
550struct AddSelectionsState {
551 above: bool,
552 stack: Vec<usize>,
553}
554
555#[derive(Clone, Debug)]
556struct SelectNextState {
557 query: AhoCorasick,
558 wordwise: bool,
559 done: bool,
560}
561
562struct BracketPairState {
563 ranges: Vec<Range<Anchor>>,
564 pair: BracketPair,
565}
566
567#[derive(Debug)]
568struct SnippetState {
569 ranges: Vec<Vec<Range<Anchor>>>,
570 active_index: usize,
571}
572
573pub struct RenameState {
574 pub range: Range<Anchor>,
575 pub old_name: Arc<str>,
576 pub editor: ViewHandle<Editor>,
577 block_id: BlockId,
578}
579
580struct InvalidationStack<T>(Vec<T>);
581
582enum ContextMenu {
583 Completions(CompletionsMenu),
584 CodeActions(CodeActionsMenu),
585}
586
587impl ContextMenu {
588 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
589 if self.visible() {
590 match self {
591 ContextMenu::Completions(menu) => menu.select_prev(cx),
592 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
593 }
594 true
595 } else {
596 false
597 }
598 }
599
600 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
601 if self.visible() {
602 match self {
603 ContextMenu::Completions(menu) => menu.select_next(cx),
604 ContextMenu::CodeActions(menu) => menu.select_next(cx),
605 }
606 true
607 } else {
608 false
609 }
610 }
611
612 fn visible(&self) -> bool {
613 match self {
614 ContextMenu::Completions(menu) => menu.visible(),
615 ContextMenu::CodeActions(menu) => menu.visible(),
616 }
617 }
618
619 fn render(
620 &self,
621 cursor_position: DisplayPoint,
622 style: EditorStyle,
623 cx: &mut RenderContext<Editor>,
624 ) -> (DisplayPoint, ElementBox) {
625 match self {
626 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
627 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
628 }
629 }
630}
631
632struct CompletionsMenu {
633 id: CompletionId,
634 initial_position: Anchor,
635 buffer: ModelHandle<Buffer>,
636 completions: Arc<[Completion]>,
637 match_candidates: Vec<StringMatchCandidate>,
638 matches: Arc<[StringMatch]>,
639 selected_item: usize,
640 list: UniformListState,
641}
642
643impl CompletionsMenu {
644 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
645 if self.selected_item > 0 {
646 self.selected_item -= 1;
647 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
648 }
649 cx.notify();
650 }
651
652 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
653 if self.selected_item + 1 < self.matches.len() {
654 self.selected_item += 1;
655 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
656 }
657 cx.notify();
658 }
659
660 fn visible(&self) -> bool {
661 !self.matches.is_empty()
662 }
663
664 fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
665 enum CompletionTag {}
666
667 let completions = self.completions.clone();
668 let matches = self.matches.clone();
669 let selected_item = self.selected_item;
670 let container_style = style.autocomplete.container;
671 UniformList::new(
672 self.list.clone(),
673 matches.len(),
674 cx,
675 move |_, range, items, cx| {
676 let start_ix = range.start;
677 for (ix, mat) in matches[range].iter().enumerate() {
678 let completion = &completions[mat.candidate_id];
679 let item_ix = start_ix + ix;
680 items.push(
681 MouseEventHandler::new::<CompletionTag, _, _>(
682 mat.candidate_id,
683 cx,
684 |state, _| {
685 let item_style = if item_ix == selected_item {
686 style.autocomplete.selected_item
687 } else if state.hovered {
688 style.autocomplete.hovered_item
689 } else {
690 style.autocomplete.item
691 };
692
693 Text::new(completion.label.text.clone(), style.text.clone())
694 .with_soft_wrap(false)
695 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
696 &completion.label.text,
697 style.text.color.into(),
698 styled_runs_for_code_label(
699 &completion.label,
700 &style.syntax,
701 ),
702 &mat.positions,
703 ))
704 .contained()
705 .with_style(item_style)
706 .boxed()
707 },
708 )
709 .with_cursor_style(CursorStyle::PointingHand)
710 .on_down(MouseButton::Left, move |_, cx| {
711 cx.dispatch_action(ConfirmCompletion {
712 item_ix: Some(item_ix),
713 });
714 })
715 .boxed(),
716 );
717 }
718 },
719 )
720 .with_width_from_item(
721 self.matches
722 .iter()
723 .enumerate()
724 .max_by_key(|(_, mat)| {
725 self.completions[mat.candidate_id]
726 .label
727 .text
728 .chars()
729 .count()
730 })
731 .map(|(ix, _)| ix),
732 )
733 .contained()
734 .with_style(container_style)
735 .boxed()
736 }
737
738 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
739 let mut matches = if let Some(query) = query {
740 fuzzy::match_strings(
741 &self.match_candidates,
742 query,
743 false,
744 100,
745 &Default::default(),
746 executor,
747 )
748 .await
749 } else {
750 self.match_candidates
751 .iter()
752 .enumerate()
753 .map(|(candidate_id, candidate)| StringMatch {
754 candidate_id,
755 score: Default::default(),
756 positions: Default::default(),
757 string: candidate.string.clone(),
758 })
759 .collect()
760 };
761 matches.sort_unstable_by_key(|mat| {
762 let completion = &self.completions[mat.candidate_id];
763 (
764 completion.lsp_completion.sort_text.as_ref(),
765 Reverse(OrderedFloat(mat.score)),
766 completion.sort_key(),
767 )
768 });
769
770 for mat in &mut matches {
771 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
772 for position in &mut mat.positions {
773 *position += filter_start;
774 }
775 }
776
777 self.matches = matches.into();
778 }
779}
780
781#[derive(Clone)]
782struct CodeActionsMenu {
783 actions: Arc<[CodeAction]>,
784 buffer: ModelHandle<Buffer>,
785 selected_item: usize,
786 list: UniformListState,
787 deployed_from_indicator: bool,
788}
789
790impl CodeActionsMenu {
791 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
792 if self.selected_item > 0 {
793 self.selected_item -= 1;
794 cx.notify()
795 }
796 }
797
798 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
799 if self.selected_item + 1 < self.actions.len() {
800 self.selected_item += 1;
801 cx.notify()
802 }
803 }
804
805 fn visible(&self) -> bool {
806 !self.actions.is_empty()
807 }
808
809 fn render(
810 &self,
811 mut cursor_position: DisplayPoint,
812 style: EditorStyle,
813 cx: &mut RenderContext<Editor>,
814 ) -> (DisplayPoint, ElementBox) {
815 enum ActionTag {}
816
817 let container_style = style.autocomplete.container;
818 let actions = self.actions.clone();
819 let selected_item = self.selected_item;
820 let element = UniformList::new(
821 self.list.clone(),
822 actions.len(),
823 cx,
824 move |_, range, items, cx| {
825 let start_ix = range.start;
826 for (ix, action) in actions[range].iter().enumerate() {
827 let item_ix = start_ix + ix;
828 items.push(
829 MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
830 let item_style = if item_ix == selected_item {
831 style.autocomplete.selected_item
832 } else if state.hovered {
833 style.autocomplete.hovered_item
834 } else {
835 style.autocomplete.item
836 };
837
838 Text::new(action.lsp_action.title.clone(), style.text.clone())
839 .with_soft_wrap(false)
840 .contained()
841 .with_style(item_style)
842 .boxed()
843 })
844 .with_cursor_style(CursorStyle::PointingHand)
845 .on_down(MouseButton::Left, move |_, cx| {
846 cx.dispatch_action(ConfirmCodeAction {
847 item_ix: Some(item_ix),
848 });
849 })
850 .boxed(),
851 );
852 }
853 },
854 )
855 .with_width_from_item(
856 self.actions
857 .iter()
858 .enumerate()
859 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
860 .map(|(ix, _)| ix),
861 )
862 .contained()
863 .with_style(container_style)
864 .boxed();
865
866 if self.deployed_from_indicator {
867 *cursor_position.column_mut() = 0;
868 }
869
870 (cursor_position, element)
871 }
872}
873
874#[derive(Debug)]
875struct ActiveDiagnosticGroup {
876 primary_range: Range<Anchor>,
877 primary_message: String,
878 blocks: HashMap<BlockId, Diagnostic>,
879 is_valid: bool,
880}
881
882#[derive(Serialize, Deserialize)]
883pub struct ClipboardSelection {
884 pub len: usize,
885 pub is_entire_line: bool,
886 pub first_line_indent: u32,
887}
888
889#[derive(Debug)]
890pub struct NavigationData {
891 // Matching offsets for anchor and scroll_top_anchor allows us to recreate the anchor if the buffer
892 // has since been closed
893 cursor_anchor: Anchor,
894 cursor_position: Point,
895 scroll_position: Vector2F,
896 scroll_top_anchor: Anchor,
897 scroll_top_row: u32,
898}
899
900pub struct EditorCreated(pub ViewHandle<Editor>);
901
902enum GotoDefinitionKind {
903 Symbol,
904 Type,
905}
906
907impl Editor {
908 pub fn single_line(
909 field_editor_style: Option<GetFieldEditorTheme>,
910 cx: &mut ViewContext<Self>,
911 ) -> Self {
912 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
913 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
914 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
915 }
916
917 pub fn auto_height(
918 max_lines: usize,
919 field_editor_style: Option<GetFieldEditorTheme>,
920 cx: &mut ViewContext<Self>,
921 ) -> Self {
922 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
923 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
924 Self::new(
925 EditorMode::AutoHeight { max_lines },
926 buffer,
927 None,
928 field_editor_style,
929 cx,
930 )
931 }
932
933 pub fn for_buffer(
934 buffer: ModelHandle<Buffer>,
935 project: Option<ModelHandle<Project>>,
936 cx: &mut ViewContext<Self>,
937 ) -> Self {
938 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
939 Self::new(EditorMode::Full, buffer, project, None, cx)
940 }
941
942 pub fn for_multibuffer(
943 buffer: ModelHandle<MultiBuffer>,
944 project: Option<ModelHandle<Project>>,
945 cx: &mut ViewContext<Self>,
946 ) -> Self {
947 Self::new(EditorMode::Full, buffer, project, None, cx)
948 }
949
950 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
951 let mut clone = Self::new(
952 self.mode,
953 self.buffer.clone(),
954 self.project.clone(),
955 self.get_field_editor_theme,
956 cx,
957 );
958 self.display_map.update(cx, |display_map, cx| {
959 let snapshot = display_map.snapshot(cx);
960 clone.display_map.update(cx, |display_map, cx| {
961 display_map.set_state(&snapshot, cx);
962 });
963 });
964 clone.selections.set_state(&self.selections);
965 clone.scroll_position = self.scroll_position;
966 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
967 clone.searchable = self.searchable;
968 clone
969 }
970
971 fn new(
972 mode: EditorMode,
973 buffer: ModelHandle<MultiBuffer>,
974 project: Option<ModelHandle<Project>>,
975 get_field_editor_theme: Option<GetFieldEditorTheme>,
976 cx: &mut ViewContext<Self>,
977 ) -> Self {
978 let display_map = cx.add_model(|cx| {
979 let settings = cx.global::<Settings>();
980 let style = build_style(&*settings, get_field_editor_theme, None, cx);
981 DisplayMap::new(
982 buffer.clone(),
983 style.text.font_id,
984 style.text.font_size,
985 None,
986 2,
987 1,
988 cx,
989 )
990 });
991
992 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
993
994 let mut this = Self {
995 handle: cx.weak_handle(),
996 buffer: buffer.clone(),
997 display_map: display_map.clone(),
998 selections,
999 columnar_selection_tail: None,
1000 add_selections_state: None,
1001 select_next_state: None,
1002 selection_history: Default::default(),
1003 autoclose_stack: Default::default(),
1004 snippet_stack: Default::default(),
1005 select_larger_syntax_node_stack: Vec::new(),
1006 ime_transaction: Default::default(),
1007 active_diagnostics: None,
1008 soft_wrap_mode_override: None,
1009 get_field_editor_theme,
1010 project,
1011 scroll_position: Vector2F::zero(),
1012 scroll_top_anchor: Anchor::min(),
1013 autoscroll_request: None,
1014 focused: false,
1015 show_local_cursors: false,
1016 show_local_selections: true,
1017 blink_epoch: 0,
1018 blinking_paused: false,
1019 mode,
1020 vertical_scroll_margin: 3.0,
1021 placeholder_text: None,
1022 highlighted_rows: None,
1023 background_highlights: Default::default(),
1024 nav_history: None,
1025 context_menu: None,
1026 mouse_context_menu: cx.add_view(|cx| context_menu::ContextMenu::new(cx)),
1027 completion_tasks: Default::default(),
1028 next_completion_id: 0,
1029 available_code_actions: Default::default(),
1030 code_actions_task: Default::default(),
1031 document_highlights_task: Default::default(),
1032 pending_rename: Default::default(),
1033 searchable: true,
1034 override_text_style: None,
1035 cursor_shape: Default::default(),
1036 keymap_context_layers: Default::default(),
1037 input_enabled: true,
1038 leader_replica_id: None,
1039 hover_state: Default::default(),
1040 link_go_to_definition_state: Default::default(),
1041 _subscriptions: vec![
1042 cx.observe(&buffer, Self::on_buffer_changed),
1043 cx.subscribe(&buffer, Self::on_buffer_event),
1044 cx.observe(&display_map, Self::on_display_map_changed),
1045 ],
1046 };
1047 this.end_selection(cx);
1048
1049 let editor_created_event = EditorCreated(cx.handle());
1050 cx.emit_global(editor_created_event);
1051
1052 this
1053 }
1054
1055 pub fn new_file(
1056 workspace: &mut Workspace,
1057 _: &workspace::NewFile,
1058 cx: &mut ViewContext<Workspace>,
1059 ) {
1060 let project = workspace.project().clone();
1061 if project.read(cx).is_remote() {
1062 cx.propagate_action();
1063 } else if let Some(buffer) = project
1064 .update(cx, |project, cx| project.create_buffer("", None, cx))
1065 .log_err()
1066 {
1067 workspace.add_item(
1068 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1069 cx,
1070 );
1071 }
1072 }
1073
1074 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1075 self.buffer.read(cx).replica_id()
1076 }
1077
1078 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1079 self.leader_replica_id
1080 }
1081
1082 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1083 &self.buffer
1084 }
1085
1086 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1087 self.buffer().read(cx).title(cx)
1088 }
1089
1090 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
1091 EditorSnapshot {
1092 mode: self.mode,
1093 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1094 scroll_position: self.scroll_position,
1095 scroll_top_anchor: self.scroll_top_anchor.clone(),
1096 placeholder_text: self.placeholder_text.clone(),
1097 is_focused: self
1098 .handle
1099 .upgrade(cx)
1100 .map_or(false, |handle| handle.is_focused(cx)),
1101 }
1102 }
1103
1104 pub fn language_at<'a, T: ToOffset>(
1105 &self,
1106 point: T,
1107 cx: &'a AppContext,
1108 ) -> Option<&'a Arc<Language>> {
1109 self.buffer.read(cx).language_at(point, cx)
1110 }
1111
1112 fn style(&self, cx: &AppContext) -> EditorStyle {
1113 build_style(
1114 cx.global::<Settings>(),
1115 self.get_field_editor_theme,
1116 self.override_text_style.as_deref(),
1117 cx,
1118 )
1119 }
1120
1121 pub fn mode(&self) -> EditorMode {
1122 self.mode
1123 }
1124
1125 pub fn set_placeholder_text(
1126 &mut self,
1127 placeholder_text: impl Into<Arc<str>>,
1128 cx: &mut ViewContext<Self>,
1129 ) {
1130 self.placeholder_text = Some(placeholder_text.into());
1131 cx.notify();
1132 }
1133
1134 pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
1135 self.vertical_scroll_margin = margin_rows as f32;
1136 cx.notify();
1137 }
1138
1139 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
1140 self.set_scroll_position_internal(scroll_position, true, cx);
1141 }
1142
1143 fn set_scroll_position_internal(
1144 &mut self,
1145 scroll_position: Vector2F,
1146 local: bool,
1147 cx: &mut ViewContext<Self>,
1148 ) {
1149 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1150
1151 if scroll_position.y() == 0. {
1152 self.scroll_top_anchor = Anchor::min();
1153 self.scroll_position = scroll_position;
1154 } else {
1155 let scroll_top_buffer_offset =
1156 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
1157 let anchor = map
1158 .buffer_snapshot
1159 .anchor_at(scroll_top_buffer_offset, Bias::Right);
1160 self.scroll_position = vec2f(
1161 scroll_position.x(),
1162 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
1163 );
1164 self.scroll_top_anchor = anchor;
1165 }
1166
1167 self.autoscroll_request.take();
1168 hide_hover(self, cx);
1169
1170 cx.emit(Event::ScrollPositionChanged { local });
1171 cx.notify();
1172 }
1173
1174 fn set_scroll_top_anchor(
1175 &mut self,
1176 anchor: Anchor,
1177 position: Vector2F,
1178 cx: &mut ViewContext<Self>,
1179 ) {
1180 self.scroll_top_anchor = anchor;
1181 self.scroll_position = position;
1182 cx.emit(Event::ScrollPositionChanged { local: false });
1183 cx.notify();
1184 }
1185
1186 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1187 self.cursor_shape = cursor_shape;
1188 cx.notify();
1189 }
1190
1191 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1192 if self.display_map.read(cx).clip_at_line_ends != clip {
1193 self.display_map
1194 .update(cx, |map, _| map.clip_at_line_ends = clip);
1195 }
1196 }
1197
1198 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: gpui::keymap::Context) {
1199 self.keymap_context_layers
1200 .insert(TypeId::of::<Tag>(), context);
1201 }
1202
1203 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1204 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1205 }
1206
1207 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1208 self.input_enabled = input_enabled;
1209 }
1210
1211 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
1212 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1213 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
1214 }
1215
1216 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
1217 if max < self.scroll_position.x() {
1218 self.scroll_position.set_x(max);
1219 true
1220 } else {
1221 false
1222 }
1223 }
1224
1225 pub fn autoscroll_vertically(
1226 &mut self,
1227 viewport_height: f32,
1228 line_height: f32,
1229 cx: &mut ViewContext<Self>,
1230 ) -> bool {
1231 let visible_lines = viewport_height / line_height;
1232 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1233 let mut scroll_position =
1234 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
1235 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1236 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
1237 } else {
1238 display_map.max_point().row().saturating_sub(1) as f32
1239 };
1240 if scroll_position.y() > max_scroll_top {
1241 scroll_position.set_y(max_scroll_top);
1242 self.set_scroll_position(scroll_position, cx);
1243 }
1244
1245 let (autoscroll, local) = if let Some(autoscroll) = self.autoscroll_request.take() {
1246 autoscroll
1247 } else {
1248 return false;
1249 };
1250
1251 let first_cursor_top;
1252 let last_cursor_bottom;
1253 if let Some(highlighted_rows) = &self.highlighted_rows {
1254 first_cursor_top = highlighted_rows.start as f32;
1255 last_cursor_bottom = first_cursor_top + 1.;
1256 } else if autoscroll == Autoscroll::Newest {
1257 let newest_selection = self.selections.newest::<Point>(cx);
1258 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
1259 last_cursor_bottom = first_cursor_top + 1.;
1260 } else {
1261 let selections = self.selections.all::<Point>(cx);
1262 first_cursor_top = selections
1263 .first()
1264 .unwrap()
1265 .head()
1266 .to_display_point(&display_map)
1267 .row() as f32;
1268 last_cursor_bottom = selections
1269 .last()
1270 .unwrap()
1271 .head()
1272 .to_display_point(&display_map)
1273 .row() as f32
1274 + 1.0;
1275 }
1276
1277 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1278 0.
1279 } else {
1280 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
1281 };
1282 if margin < 0.0 {
1283 return false;
1284 }
1285
1286 match autoscroll {
1287 Autoscroll::Fit | Autoscroll::Newest => {
1288 let margin = margin.min(self.vertical_scroll_margin);
1289 let target_top = (first_cursor_top - margin).max(0.0);
1290 let target_bottom = last_cursor_bottom + margin;
1291 let start_row = scroll_position.y();
1292 let end_row = start_row + visible_lines;
1293
1294 if target_top < start_row {
1295 scroll_position.set_y(target_top);
1296 self.set_scroll_position_internal(scroll_position, local, cx);
1297 } else if target_bottom >= end_row {
1298 scroll_position.set_y(target_bottom - visible_lines);
1299 self.set_scroll_position_internal(scroll_position, local, cx);
1300 }
1301 }
1302 Autoscroll::Center => {
1303 scroll_position.set_y((first_cursor_top - margin).max(0.0));
1304 self.set_scroll_position_internal(scroll_position, local, cx);
1305 }
1306 }
1307
1308 true
1309 }
1310
1311 pub fn autoscroll_horizontally(
1312 &mut self,
1313 start_row: u32,
1314 viewport_width: f32,
1315 scroll_width: f32,
1316 max_glyph_width: f32,
1317 layouts: &[text_layout::Line],
1318 cx: &mut ViewContext<Self>,
1319 ) -> bool {
1320 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1321 let selections = self.selections.all::<Point>(cx);
1322
1323 let mut target_left;
1324 let mut target_right;
1325
1326 if self.highlighted_rows.is_some() {
1327 target_left = 0.0_f32;
1328 target_right = 0.0_f32;
1329 } else {
1330 target_left = std::f32::INFINITY;
1331 target_right = 0.0_f32;
1332 for selection in selections {
1333 let head = selection.head().to_display_point(&display_map);
1334 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
1335 let start_column = head.column().saturating_sub(3);
1336 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
1337 target_left = target_left.min(
1338 layouts[(head.row() - start_row) as usize]
1339 .x_for_index(start_column as usize),
1340 );
1341 target_right = target_right.max(
1342 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
1343 + max_glyph_width,
1344 );
1345 }
1346 }
1347 }
1348
1349 target_right = target_right.min(scroll_width);
1350
1351 if target_right - target_left > viewport_width {
1352 return false;
1353 }
1354
1355 let scroll_left = self.scroll_position.x() * max_glyph_width;
1356 let scroll_right = scroll_left + viewport_width;
1357
1358 if target_left < scroll_left {
1359 self.scroll_position.set_x(target_left / max_glyph_width);
1360 true
1361 } else if target_right > scroll_right {
1362 self.scroll_position
1363 .set_x((target_right - viewport_width) / max_glyph_width);
1364 true
1365 } else {
1366 false
1367 }
1368 }
1369
1370 fn selections_did_change(
1371 &mut self,
1372 local: bool,
1373 old_cursor_position: &Anchor,
1374 cx: &mut ViewContext<Self>,
1375 ) {
1376 if self.focused && self.leader_replica_id.is_none() {
1377 self.buffer.update(cx, |buffer, cx| {
1378 buffer.set_active_selections(
1379 &self.selections.disjoint_anchors(),
1380 self.selections.line_mode,
1381 cx,
1382 )
1383 });
1384 }
1385
1386 let display_map = self
1387 .display_map
1388 .update(cx, |display_map, cx| display_map.snapshot(cx));
1389 let buffer = &display_map.buffer_snapshot;
1390 self.add_selections_state = None;
1391 self.select_next_state = None;
1392 self.select_larger_syntax_node_stack.clear();
1393 self.autoclose_stack
1394 .invalidate(&self.selections.disjoint_anchors(), buffer);
1395 self.snippet_stack
1396 .invalidate(&self.selections.disjoint_anchors(), buffer);
1397 self.take_rename(false, cx);
1398
1399 let new_cursor_position = self.selections.newest_anchor().head();
1400
1401 self.push_to_nav_history(
1402 old_cursor_position.clone(),
1403 Some(new_cursor_position.to_point(buffer)),
1404 cx,
1405 );
1406
1407 if local {
1408 let new_cursor_position = self.selections.newest_anchor().head();
1409 let completion_menu = match self.context_menu.as_mut() {
1410 Some(ContextMenu::Completions(menu)) => Some(menu),
1411 _ => {
1412 self.context_menu.take();
1413 None
1414 }
1415 };
1416
1417 if let Some(completion_menu) = completion_menu {
1418 let cursor_position = new_cursor_position.to_offset(buffer);
1419 let (word_range, kind) =
1420 buffer.surrounding_word(completion_menu.initial_position.clone());
1421 if kind == Some(CharKind::Word)
1422 && word_range.to_inclusive().contains(&cursor_position)
1423 {
1424 let query = Self::completion_query(buffer, cursor_position);
1425 cx.background()
1426 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1427 self.show_completions(&ShowCompletions, cx);
1428 } else {
1429 self.hide_context_menu(cx);
1430 }
1431 }
1432
1433 hide_hover(self, cx);
1434
1435 if old_cursor_position.to_display_point(&display_map).row()
1436 != new_cursor_position.to_display_point(&display_map).row()
1437 {
1438 self.available_code_actions.take();
1439 }
1440 self.refresh_code_actions(cx);
1441 self.refresh_document_highlights(cx);
1442 refresh_matching_bracket_highlights(self, cx);
1443 }
1444
1445 self.pause_cursor_blinking(cx);
1446 cx.emit(Event::SelectionsChanged { local });
1447 cx.notify();
1448 }
1449
1450 pub fn change_selections<R>(
1451 &mut self,
1452 autoscroll: Option<Autoscroll>,
1453 cx: &mut ViewContext<Self>,
1454 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1455 ) -> R {
1456 let old_cursor_position = self.selections.newest_anchor().head();
1457 self.push_to_selection_history();
1458
1459 let (changed, result) = self.selections.change_with(cx, change);
1460
1461 if changed {
1462 if let Some(autoscroll) = autoscroll {
1463 self.request_autoscroll(autoscroll, cx);
1464 }
1465 self.selections_did_change(true, &old_cursor_position, cx);
1466 }
1467
1468 result
1469 }
1470
1471 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1472 where
1473 I: IntoIterator<Item = (Range<S>, T)>,
1474 S: ToOffset,
1475 T: Into<Arc<str>>,
1476 {
1477 self.buffer
1478 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1479 }
1480
1481 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1482 where
1483 I: IntoIterator<Item = (Range<S>, T)>,
1484 S: ToOffset,
1485 T: Into<Arc<str>>,
1486 {
1487 self.buffer.update(cx, |buffer, cx| {
1488 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1489 });
1490 }
1491
1492 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1493 self.hide_context_menu(cx);
1494
1495 match phase {
1496 SelectPhase::Begin {
1497 position,
1498 add,
1499 click_count,
1500 } => self.begin_selection(*position, *add, *click_count, cx),
1501 SelectPhase::BeginColumnar {
1502 position,
1503 goal_column,
1504 } => self.begin_columnar_selection(*position, *goal_column, cx),
1505 SelectPhase::Extend {
1506 position,
1507 click_count,
1508 } => self.extend_selection(*position, *click_count, cx),
1509 SelectPhase::Update {
1510 position,
1511 goal_column,
1512 scroll_position,
1513 } => self.update_selection(*position, *goal_column, *scroll_position, cx),
1514 SelectPhase::End => self.end_selection(cx),
1515 }
1516 }
1517
1518 fn extend_selection(
1519 &mut self,
1520 position: DisplayPoint,
1521 click_count: usize,
1522 cx: &mut ViewContext<Self>,
1523 ) {
1524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1525 let tail = self.selections.newest::<usize>(cx).tail();
1526 self.begin_selection(position, false, click_count, cx);
1527
1528 let position = position.to_offset(&display_map, Bias::Left);
1529 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1530
1531 let mut pending_selection = self
1532 .selections
1533 .pending_anchor()
1534 .expect("extend_selection not called with pending selection");
1535 if position >= tail {
1536 pending_selection.start = tail_anchor.clone();
1537 } else {
1538 pending_selection.end = tail_anchor.clone();
1539 pending_selection.reversed = true;
1540 }
1541
1542 let mut pending_mode = self.selections.pending_mode().unwrap();
1543 match &mut pending_mode {
1544 SelectMode::Word(range) | SelectMode::Line(range) => {
1545 *range = tail_anchor.clone()..tail_anchor
1546 }
1547 _ => {}
1548 }
1549
1550 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
1551 s.set_pending(pending_selection, pending_mode)
1552 });
1553 }
1554
1555 fn begin_selection(
1556 &mut self,
1557 position: DisplayPoint,
1558 add: bool,
1559 click_count: usize,
1560 cx: &mut ViewContext<Self>,
1561 ) {
1562 if !self.focused {
1563 cx.focus_self();
1564 cx.emit(Event::Activate);
1565 }
1566
1567 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1568 let buffer = &display_map.buffer_snapshot;
1569 let newest_selection = self.selections.newest_anchor().clone();
1570 let position = display_map.clip_point(position, Bias::Left);
1571
1572 let start;
1573 let end;
1574 let mode;
1575 match click_count {
1576 1 => {
1577 start = buffer.anchor_before(position.to_point(&display_map));
1578 end = start.clone();
1579 mode = SelectMode::Character;
1580 }
1581 2 => {
1582 let range = movement::surrounding_word(&display_map, position);
1583 start = buffer.anchor_before(range.start.to_point(&display_map));
1584 end = buffer.anchor_before(range.end.to_point(&display_map));
1585 mode = SelectMode::Word(start.clone()..end.clone());
1586 }
1587 3 => {
1588 let position = display_map
1589 .clip_point(position, Bias::Left)
1590 .to_point(&display_map);
1591 let line_start = display_map.prev_line_boundary(position).0;
1592 let next_line_start = buffer.clip_point(
1593 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1594 Bias::Left,
1595 );
1596 start = buffer.anchor_before(line_start);
1597 end = buffer.anchor_before(next_line_start);
1598 mode = SelectMode::Line(start.clone()..end.clone());
1599 }
1600 _ => {
1601 start = buffer.anchor_before(0);
1602 end = buffer.anchor_before(buffer.len());
1603 mode = SelectMode::All;
1604 }
1605 }
1606
1607 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
1608 if !add {
1609 s.clear_disjoint();
1610 } else if click_count > 1 {
1611 s.delete(newest_selection.id)
1612 }
1613
1614 s.set_pending_anchor_range(start..end, mode);
1615 });
1616 }
1617
1618 fn begin_columnar_selection(
1619 &mut self,
1620 position: DisplayPoint,
1621 goal_column: u32,
1622 cx: &mut ViewContext<Self>,
1623 ) {
1624 if !self.focused {
1625 cx.focus_self();
1626 cx.emit(Event::Activate);
1627 }
1628
1629 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1630 let tail = self.selections.newest::<Point>(cx).tail();
1631 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1632
1633 self.select_columns(
1634 tail.to_display_point(&display_map),
1635 position,
1636 goal_column,
1637 &display_map,
1638 cx,
1639 );
1640 }
1641
1642 fn update_selection(
1643 &mut self,
1644 position: DisplayPoint,
1645 goal_column: u32,
1646 scroll_position: Vector2F,
1647 cx: &mut ViewContext<Self>,
1648 ) {
1649 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1650
1651 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1652 let tail = tail.to_display_point(&display_map);
1653 self.select_columns(tail, position, goal_column, &display_map, cx);
1654 } else if let Some(mut pending) = self.selections.pending_anchor().clone() {
1655 let buffer = self.buffer.read(cx).snapshot(cx);
1656 let head;
1657 let tail;
1658 let mode = self.selections.pending_mode().unwrap();
1659 match &mode {
1660 SelectMode::Character => {
1661 head = position.to_point(&display_map);
1662 tail = pending.tail().to_point(&buffer);
1663 }
1664 SelectMode::Word(original_range) => {
1665 let original_display_range = original_range.start.to_display_point(&display_map)
1666 ..original_range.end.to_display_point(&display_map);
1667 let original_buffer_range = original_display_range.start.to_point(&display_map)
1668 ..original_display_range.end.to_point(&display_map);
1669 if movement::is_inside_word(&display_map, position)
1670 || original_display_range.contains(&position)
1671 {
1672 let word_range = movement::surrounding_word(&display_map, position);
1673 if word_range.start < original_display_range.start {
1674 head = word_range.start.to_point(&display_map);
1675 } else {
1676 head = word_range.end.to_point(&display_map);
1677 }
1678 } else {
1679 head = position.to_point(&display_map);
1680 }
1681
1682 if head <= original_buffer_range.start {
1683 tail = original_buffer_range.end;
1684 } else {
1685 tail = original_buffer_range.start;
1686 }
1687 }
1688 SelectMode::Line(original_range) => {
1689 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1690
1691 let position = display_map
1692 .clip_point(position, Bias::Left)
1693 .to_point(&display_map);
1694 let line_start = display_map.prev_line_boundary(position).0;
1695 let next_line_start = buffer.clip_point(
1696 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1697 Bias::Left,
1698 );
1699
1700 if line_start < original_range.start {
1701 head = line_start
1702 } else {
1703 head = next_line_start
1704 }
1705
1706 if head <= original_range.start {
1707 tail = original_range.end;
1708 } else {
1709 tail = original_range.start;
1710 }
1711 }
1712 SelectMode::All => {
1713 return;
1714 }
1715 };
1716
1717 if head < tail {
1718 pending.start = buffer.anchor_before(head);
1719 pending.end = buffer.anchor_before(tail);
1720 pending.reversed = true;
1721 } else {
1722 pending.start = buffer.anchor_before(tail);
1723 pending.end = buffer.anchor_before(head);
1724 pending.reversed = false;
1725 }
1726
1727 self.change_selections(None, cx, |s| {
1728 s.set_pending(pending, mode);
1729 });
1730 } else {
1731 log::error!("update_selection dispatched with no pending selection");
1732 return;
1733 }
1734
1735 self.set_scroll_position(scroll_position, cx);
1736 cx.notify();
1737 }
1738
1739 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1740 self.columnar_selection_tail.take();
1741 if self.selections.pending_anchor().is_some() {
1742 let selections = self.selections.all::<usize>(cx);
1743 self.change_selections(None, cx, |s| {
1744 s.select(selections);
1745 s.clear_pending();
1746 });
1747 }
1748 }
1749
1750 fn select_columns(
1751 &mut self,
1752 tail: DisplayPoint,
1753 head: DisplayPoint,
1754 goal_column: u32,
1755 display_map: &DisplaySnapshot,
1756 cx: &mut ViewContext<Self>,
1757 ) {
1758 let start_row = cmp::min(tail.row(), head.row());
1759 let end_row = cmp::max(tail.row(), head.row());
1760 let start_column = cmp::min(tail.column(), goal_column);
1761 let end_column = cmp::max(tail.column(), goal_column);
1762 let reversed = start_column < tail.column();
1763
1764 let selection_ranges = (start_row..=end_row)
1765 .filter_map(|row| {
1766 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1767 let start = display_map
1768 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1769 .to_point(&display_map);
1770 let end = display_map
1771 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1772 .to_point(&display_map);
1773 if reversed {
1774 Some(end..start)
1775 } else {
1776 Some(start..end)
1777 }
1778 } else {
1779 None
1780 }
1781 })
1782 .collect::<Vec<_>>();
1783
1784 self.change_selections(None, cx, |s| {
1785 s.select_ranges(selection_ranges);
1786 });
1787 cx.notify();
1788 }
1789
1790 pub fn 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 the selection is non-empty, then increase the indentation of the selected lines.
2983 if !selection.is_empty() {
2984 row_delta =
2985 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
2986 continue;
2987 }
2988
2989 // If the selection is empty and the cursor is in the leading whitespace before the
2990 // suggested indentation, then auto-indent the line.
2991 let cursor = selection.head();
2992 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
2993 let current_indent = snapshot.indent_size_for_line(cursor.row);
2994 if cursor.column < suggested_indent.len
2995 && cursor.column <= current_indent.len
2996 && current_indent.len <= suggested_indent.len
2997 {
2998 selection.start = Point::new(cursor.row, suggested_indent.len);
2999 selection.end = selection.start;
3000 if row_delta == 0 {
3001 edits.extend(Buffer::edit_for_indent_size_adjustment(
3002 cursor.row,
3003 current_indent,
3004 suggested_indent,
3005 ));
3006 row_delta = suggested_indent.len - current_indent.len;
3007 }
3008 continue;
3009 }
3010 }
3011
3012 // Otherwise, insert a hard or soft tab.
3013 let settings = cx.global::<Settings>();
3014 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3015 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3016 IndentSize::tab()
3017 } else {
3018 let tab_size = settings.tab_size(language_name.as_deref()).get();
3019 let char_column = snapshot
3020 .text_for_range(Point::new(cursor.row, 0)..cursor)
3021 .flat_map(str::chars)
3022 .count()
3023 + row_delta as usize;
3024 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3025 IndentSize::spaces(chars_to_next_tab_stop)
3026 };
3027 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3028 selection.end = selection.start;
3029 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3030 row_delta += tab_size.len;
3031 }
3032
3033 self.transact(cx, |this, cx| {
3034 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3035 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections))
3036 });
3037 }
3038
3039 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3040 let mut selections = self.selections.all::<Point>(cx);
3041 let mut prev_edited_row = 0;
3042 let mut row_delta = 0;
3043 let mut edits = Vec::new();
3044 let buffer = self.buffer.read(cx);
3045 let snapshot = buffer.snapshot(cx);
3046 for selection in &mut selections {
3047 if selection.start.row != prev_edited_row {
3048 row_delta = 0;
3049 }
3050 prev_edited_row = selection.end.row;
3051
3052 row_delta =
3053 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3054 }
3055
3056 self.transact(cx, |this, cx| {
3057 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3058 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3059 });
3060 }
3061
3062 fn indent_selection(
3063 buffer: &MultiBuffer,
3064 snapshot: &MultiBufferSnapshot,
3065 selection: &mut Selection<Point>,
3066 edits: &mut Vec<(Range<Point>, String)>,
3067 delta_for_start_row: u32,
3068 cx: &AppContext,
3069 ) -> u32 {
3070 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3071 let settings = cx.global::<Settings>();
3072 let tab_size = settings.tab_size(language_name.as_deref()).get();
3073 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3074 IndentKind::Tab
3075 } else {
3076 IndentKind::Space
3077 };
3078 let mut start_row = selection.start.row;
3079 let mut end_row = selection.end.row + 1;
3080
3081 // If a selection ends at the beginning of a line, don't indent
3082 // that last line.
3083 if selection.end.column == 0 {
3084 end_row -= 1;
3085 }
3086
3087 // Avoid re-indenting a row that has already been indented by a
3088 // previous selection, but still update this selection's column
3089 // to reflect that indentation.
3090 if delta_for_start_row > 0 {
3091 start_row += 1;
3092 selection.start.column += delta_for_start_row;
3093 if selection.end.row == selection.start.row {
3094 selection.end.column += delta_for_start_row;
3095 }
3096 }
3097
3098 let mut delta_for_end_row = 0;
3099 for row in start_row..end_row {
3100 let current_indent = snapshot.indent_size_for_line(row);
3101 let indent_delta = match (current_indent.kind, indent_kind) {
3102 (IndentKind::Space, IndentKind::Space) => {
3103 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3104 IndentSize::spaces(columns_to_next_tab_stop)
3105 }
3106 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3107 (_, IndentKind::Tab) => IndentSize::tab(),
3108 };
3109
3110 let row_start = Point::new(row, 0);
3111 edits.push((
3112 row_start..row_start,
3113 indent_delta.chars().collect::<String>(),
3114 ));
3115
3116 // Update this selection's endpoints to reflect the indentation.
3117 if row == selection.start.row {
3118 selection.start.column += indent_delta.len;
3119 }
3120 if row == selection.end.row {
3121 selection.end.column += indent_delta.len;
3122 delta_for_end_row = indent_delta.len;
3123 }
3124 }
3125
3126 if selection.start.row == selection.end.row {
3127 delta_for_start_row + delta_for_end_row
3128 } else {
3129 delta_for_end_row
3130 }
3131 }
3132
3133 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3134 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3135 let selections = self.selections.all::<Point>(cx);
3136 let mut deletion_ranges = Vec::new();
3137 let mut last_outdent = None;
3138 {
3139 let buffer = self.buffer.read(cx);
3140 let snapshot = buffer.snapshot(cx);
3141 for selection in &selections {
3142 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3143 let tab_size = cx
3144 .global::<Settings>()
3145 .tab_size(language_name.as_deref())
3146 .get();
3147 let mut rows = selection.spanned_rows(false, &display_map);
3148
3149 // Avoid re-outdenting a row that has already been outdented by a
3150 // previous selection.
3151 if let Some(last_row) = last_outdent {
3152 if last_row == rows.start {
3153 rows.start += 1;
3154 }
3155 }
3156
3157 for row in rows {
3158 let indent_size = snapshot.indent_size_for_line(row);
3159 if indent_size.len > 0 {
3160 let deletion_len = match indent_size.kind {
3161 IndentKind::Space => {
3162 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3163 if columns_to_prev_tab_stop == 0 {
3164 tab_size
3165 } else {
3166 columns_to_prev_tab_stop
3167 }
3168 }
3169 IndentKind::Tab => 1,
3170 };
3171 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3172 last_outdent = Some(row);
3173 }
3174 }
3175 }
3176 }
3177
3178 self.transact(cx, |this, cx| {
3179 this.buffer.update(cx, |buffer, cx| {
3180 let empty_str: Arc<str> = "".into();
3181 buffer.edit(
3182 deletion_ranges
3183 .into_iter()
3184 .map(|range| (range, empty_str.clone())),
3185 None,
3186 cx,
3187 );
3188 });
3189 let selections = this.selections.all::<usize>(cx);
3190 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3191 });
3192 }
3193
3194 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3195 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3196 let selections = self.selections.all::<Point>(cx);
3197
3198 let mut new_cursors = Vec::new();
3199 let mut edit_ranges = Vec::new();
3200 let mut selections = selections.iter().peekable();
3201 while let Some(selection) = selections.next() {
3202 let mut rows = selection.spanned_rows(false, &display_map);
3203 let goal_display_column = selection.head().to_display_point(&display_map).column();
3204
3205 // Accumulate contiguous regions of rows that we want to delete.
3206 while let Some(next_selection) = selections.peek() {
3207 let next_rows = next_selection.spanned_rows(false, &display_map);
3208 if next_rows.start <= rows.end {
3209 rows.end = next_rows.end;
3210 selections.next().unwrap();
3211 } else {
3212 break;
3213 }
3214 }
3215
3216 let buffer = &display_map.buffer_snapshot;
3217 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3218 let edit_end;
3219 let cursor_buffer_row;
3220 if buffer.max_point().row >= rows.end {
3221 // If there's a line after the range, delete the \n from the end of the row range
3222 // and position the cursor on the next line.
3223 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3224 cursor_buffer_row = rows.end;
3225 } else {
3226 // If there isn't a line after the range, delete the \n from the line before the
3227 // start of the row range and position the cursor there.
3228 edit_start = edit_start.saturating_sub(1);
3229 edit_end = buffer.len();
3230 cursor_buffer_row = rows.start.saturating_sub(1);
3231 }
3232
3233 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3234 *cursor.column_mut() =
3235 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3236
3237 new_cursors.push((
3238 selection.id,
3239 buffer.anchor_after(cursor.to_point(&display_map)),
3240 ));
3241 edit_ranges.push(edit_start..edit_end);
3242 }
3243
3244 self.transact(cx, |this, cx| {
3245 let buffer = this.buffer.update(cx, |buffer, cx| {
3246 let empty_str: Arc<str> = "".into();
3247 buffer.edit(
3248 edit_ranges
3249 .into_iter()
3250 .map(|range| (range, empty_str.clone())),
3251 None,
3252 cx,
3253 );
3254 buffer.snapshot(cx)
3255 });
3256 let new_selections = new_cursors
3257 .into_iter()
3258 .map(|(id, cursor)| {
3259 let cursor = cursor.to_point(&buffer);
3260 Selection {
3261 id,
3262 start: cursor,
3263 end: cursor,
3264 reversed: false,
3265 goal: SelectionGoal::None,
3266 }
3267 })
3268 .collect();
3269
3270 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3271 s.select(new_selections);
3272 });
3273 });
3274 }
3275
3276 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3277 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3278 let buffer = &display_map.buffer_snapshot;
3279 let selections = self.selections.all::<Point>(cx);
3280
3281 let mut edits = Vec::new();
3282 let mut selections_iter = selections.iter().peekable();
3283 while let Some(selection) = selections_iter.next() {
3284 // Avoid duplicating the same lines twice.
3285 let mut rows = selection.spanned_rows(false, &display_map);
3286
3287 while let Some(next_selection) = selections_iter.peek() {
3288 let next_rows = next_selection.spanned_rows(false, &display_map);
3289 if next_rows.start <= rows.end - 1 {
3290 rows.end = next_rows.end;
3291 selections_iter.next().unwrap();
3292 } else {
3293 break;
3294 }
3295 }
3296
3297 // Copy the text from the selected row region and splice it at the start of the region.
3298 let start = Point::new(rows.start, 0);
3299 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3300 let text = buffer
3301 .text_for_range(start..end)
3302 .chain(Some("\n"))
3303 .collect::<String>();
3304 edits.push((start..start, text));
3305 }
3306
3307 self.transact(cx, |this, cx| {
3308 this.buffer.update(cx, |buffer, cx| {
3309 buffer.edit(edits, None, cx);
3310 });
3311
3312 this.request_autoscroll(Autoscroll::Fit, cx);
3313 });
3314 }
3315
3316 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3317 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3318 let buffer = self.buffer.read(cx).snapshot(cx);
3319
3320 let mut edits = Vec::new();
3321 let mut unfold_ranges = Vec::new();
3322 let mut refold_ranges = Vec::new();
3323
3324 let selections = self.selections.all::<Point>(cx);
3325 let mut selections = selections.iter().peekable();
3326 let mut contiguous_row_selections = Vec::new();
3327 let mut new_selections = Vec::new();
3328
3329 while let Some(selection) = selections.next() {
3330 // Find all the selections that span a contiguous row range
3331 contiguous_row_selections.push(selection.clone());
3332 let start_row = selection.start.row;
3333 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3334 display_map.next_line_boundary(selection.end).0.row + 1
3335 } else {
3336 selection.end.row
3337 };
3338
3339 while let Some(next_selection) = selections.peek() {
3340 if next_selection.start.row <= end_row {
3341 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3342 display_map.next_line_boundary(next_selection.end).0.row + 1
3343 } else {
3344 next_selection.end.row
3345 };
3346 contiguous_row_selections.push(selections.next().unwrap().clone());
3347 } else {
3348 break;
3349 }
3350 }
3351
3352 // Move the text spanned by the row range to be before the line preceding the row range
3353 if start_row > 0 {
3354 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3355 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3356 let insertion_point = display_map
3357 .prev_line_boundary(Point::new(start_row - 1, 0))
3358 .0;
3359
3360 // Don't move lines across excerpts
3361 if buffer
3362 .excerpt_boundaries_in_range((
3363 Bound::Excluded(insertion_point),
3364 Bound::Included(range_to_move.end),
3365 ))
3366 .next()
3367 .is_none()
3368 {
3369 let text = buffer
3370 .text_for_range(range_to_move.clone())
3371 .flat_map(|s| s.chars())
3372 .skip(1)
3373 .chain(['\n'])
3374 .collect::<String>();
3375
3376 edits.push((
3377 buffer.anchor_after(range_to_move.start)
3378 ..buffer.anchor_before(range_to_move.end),
3379 String::new(),
3380 ));
3381 let insertion_anchor = buffer.anchor_after(insertion_point);
3382 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3383
3384 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3385
3386 // Move selections up
3387 new_selections.extend(contiguous_row_selections.drain(..).map(
3388 |mut selection| {
3389 selection.start.row -= row_delta;
3390 selection.end.row -= row_delta;
3391 selection
3392 },
3393 ));
3394
3395 // Move folds up
3396 unfold_ranges.push(range_to_move.clone());
3397 for fold in display_map.folds_in_range(
3398 buffer.anchor_before(range_to_move.start)
3399 ..buffer.anchor_after(range_to_move.end),
3400 ) {
3401 let mut start = fold.start.to_point(&buffer);
3402 let mut end = fold.end.to_point(&buffer);
3403 start.row -= row_delta;
3404 end.row -= row_delta;
3405 refold_ranges.push(start..end);
3406 }
3407 }
3408 }
3409
3410 // If we didn't move line(s), preserve the existing selections
3411 new_selections.extend(contiguous_row_selections.drain(..));
3412 }
3413
3414 self.transact(cx, |this, cx| {
3415 this.unfold_ranges(unfold_ranges, true, cx);
3416 this.buffer.update(cx, |buffer, cx| {
3417 for (range, text) in edits {
3418 buffer.edit([(range, text)], None, cx);
3419 }
3420 });
3421 this.fold_ranges(refold_ranges, cx);
3422 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3423 s.select(new_selections);
3424 })
3425 });
3426 }
3427
3428 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3429 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3430 let buffer = self.buffer.read(cx).snapshot(cx);
3431
3432 let mut edits = Vec::new();
3433 let mut unfold_ranges = Vec::new();
3434 let mut refold_ranges = Vec::new();
3435
3436 let selections = self.selections.all::<Point>(cx);
3437 let mut selections = selections.iter().peekable();
3438 let mut contiguous_row_selections = Vec::new();
3439 let mut new_selections = Vec::new();
3440
3441 while let Some(selection) = selections.next() {
3442 // Find all the selections that span a contiguous row range
3443 contiguous_row_selections.push(selection.clone());
3444 let start_row = selection.start.row;
3445 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3446 display_map.next_line_boundary(selection.end).0.row + 1
3447 } else {
3448 selection.end.row
3449 };
3450
3451 while let Some(next_selection) = selections.peek() {
3452 if next_selection.start.row <= end_row {
3453 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3454 display_map.next_line_boundary(next_selection.end).0.row + 1
3455 } else {
3456 next_selection.end.row
3457 };
3458 contiguous_row_selections.push(selections.next().unwrap().clone());
3459 } else {
3460 break;
3461 }
3462 }
3463
3464 // Move the text spanned by the row range to be after the last line of the row range
3465 if end_row <= buffer.max_point().row {
3466 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3467 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3468
3469 // Don't move lines across excerpt boundaries
3470 if buffer
3471 .excerpt_boundaries_in_range((
3472 Bound::Excluded(range_to_move.start),
3473 Bound::Included(insertion_point),
3474 ))
3475 .next()
3476 .is_none()
3477 {
3478 let mut text = String::from("\n");
3479 text.extend(buffer.text_for_range(range_to_move.clone()));
3480 text.pop(); // Drop trailing newline
3481 edits.push((
3482 buffer.anchor_after(range_to_move.start)
3483 ..buffer.anchor_before(range_to_move.end),
3484 String::new(),
3485 ));
3486 let insertion_anchor = buffer.anchor_after(insertion_point);
3487 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3488
3489 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3490
3491 // Move selections down
3492 new_selections.extend(contiguous_row_selections.drain(..).map(
3493 |mut selection| {
3494 selection.start.row += row_delta;
3495 selection.end.row += row_delta;
3496 selection
3497 },
3498 ));
3499
3500 // Move folds down
3501 unfold_ranges.push(range_to_move.clone());
3502 for fold in display_map.folds_in_range(
3503 buffer.anchor_before(range_to_move.start)
3504 ..buffer.anchor_after(range_to_move.end),
3505 ) {
3506 let mut start = fold.start.to_point(&buffer);
3507 let mut end = fold.end.to_point(&buffer);
3508 start.row += row_delta;
3509 end.row += row_delta;
3510 refold_ranges.push(start..end);
3511 }
3512 }
3513 }
3514
3515 // If we didn't move line(s), preserve the existing selections
3516 new_selections.extend(contiguous_row_selections.drain(..));
3517 }
3518
3519 self.transact(cx, |this, cx| {
3520 this.unfold_ranges(unfold_ranges, true, cx);
3521 this.buffer.update(cx, |buffer, cx| {
3522 for (range, text) in edits {
3523 buffer.edit([(range, text)], None, cx);
3524 }
3525 });
3526 this.fold_ranges(refold_ranges, cx);
3527 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3528 });
3529 }
3530
3531 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3532 self.transact(cx, |this, cx| {
3533 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3534 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3535 let line_mode = s.line_mode;
3536 s.move_with(|display_map, selection| {
3537 if !selection.is_empty() || line_mode {
3538 return;
3539 }
3540
3541 let mut head = selection.head();
3542 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3543 if head.column() == display_map.line_len(head.row()) {
3544 transpose_offset = display_map
3545 .buffer_snapshot
3546 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3547 }
3548
3549 if transpose_offset == 0 {
3550 return;
3551 }
3552
3553 *head.column_mut() += 1;
3554 head = display_map.clip_point(head, Bias::Right);
3555 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3556
3557 let transpose_start = display_map
3558 .buffer_snapshot
3559 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3560 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3561 let transpose_end = display_map
3562 .buffer_snapshot
3563 .clip_offset(transpose_offset + 1, Bias::Right);
3564 if let Some(ch) =
3565 display_map.buffer_snapshot.chars_at(transpose_start).next()
3566 {
3567 edits.push((transpose_start..transpose_offset, String::new()));
3568 edits.push((transpose_end..transpose_end, ch.to_string()));
3569 }
3570 }
3571 });
3572 edits
3573 });
3574 this.buffer
3575 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3576 let selections = this.selections.all::<usize>(cx);
3577 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3578 s.select(selections);
3579 });
3580 });
3581 }
3582
3583 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3584 let mut text = String::new();
3585 let buffer = self.buffer.read(cx).snapshot(cx);
3586 let mut selections = self.selections.all::<Point>(cx);
3587 let mut clipboard_selections = Vec::with_capacity(selections.len());
3588 {
3589 let max_point = buffer.max_point();
3590 for selection in &mut selections {
3591 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3592 if is_entire_line {
3593 selection.start = Point::new(selection.start.row, 0);
3594 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3595 selection.goal = SelectionGoal::None;
3596 }
3597 let mut len = 0;
3598 for chunk in buffer.text_for_range(selection.start..selection.end) {
3599 text.push_str(chunk);
3600 len += chunk.len();
3601 }
3602 clipboard_selections.push(ClipboardSelection {
3603 len,
3604 is_entire_line,
3605 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3606 });
3607 }
3608 }
3609
3610 self.transact(cx, |this, cx| {
3611 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3612 s.select(selections);
3613 });
3614 this.insert("", cx);
3615 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3616 });
3617 }
3618
3619 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3620 let selections = self.selections.all::<Point>(cx);
3621 let buffer = self.buffer.read(cx).read(cx);
3622 let mut text = String::new();
3623
3624 let mut clipboard_selections = Vec::with_capacity(selections.len());
3625 {
3626 let max_point = buffer.max_point();
3627 for selection in selections.iter() {
3628 let mut start = selection.start;
3629 let mut end = selection.end;
3630 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3631 if is_entire_line {
3632 start = Point::new(start.row, 0);
3633 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3634 }
3635 let mut len = 0;
3636 for chunk in buffer.text_for_range(start..end) {
3637 text.push_str(chunk);
3638 len += chunk.len();
3639 }
3640 clipboard_selections.push(ClipboardSelection {
3641 len,
3642 is_entire_line,
3643 first_line_indent: buffer.indent_size_for_line(start.row).len,
3644 });
3645 }
3646 }
3647
3648 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3649 }
3650
3651 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3652 self.transact(cx, |this, cx| {
3653 if let Some(item) = cx.as_mut().read_from_clipboard() {
3654 let mut clipboard_text = Cow::Borrowed(item.text());
3655 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3656 let old_selections = this.selections.all::<usize>(cx);
3657 let all_selections_were_entire_line =
3658 clipboard_selections.iter().all(|s| s.is_entire_line);
3659 let first_selection_indent_column =
3660 clipboard_selections.first().map(|s| s.first_line_indent);
3661 if clipboard_selections.len() != old_selections.len() {
3662 let mut newline_separated_text = String::new();
3663 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3664 let mut ix = 0;
3665 while let Some(clipboard_selection) = clipboard_selections.next() {
3666 newline_separated_text
3667 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3668 ix += clipboard_selection.len;
3669 if clipboard_selections.peek().is_some() {
3670 newline_separated_text.push('\n');
3671 }
3672 }
3673 clipboard_text = Cow::Owned(newline_separated_text);
3674 }
3675
3676 this.buffer.update(cx, |buffer, cx| {
3677 let snapshot = buffer.read(cx);
3678 let mut start_offset = 0;
3679 let mut edits = Vec::new();
3680 let mut original_indent_columns = Vec::new();
3681 let line_mode = this.selections.line_mode;
3682 for (ix, selection) in old_selections.iter().enumerate() {
3683 let to_insert;
3684 let entire_line;
3685 let original_indent_column;
3686 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3687 let end_offset = start_offset + clipboard_selection.len;
3688 to_insert = &clipboard_text[start_offset..end_offset];
3689 entire_line = clipboard_selection.is_entire_line;
3690 start_offset = end_offset;
3691 original_indent_column =
3692 Some(clipboard_selection.first_line_indent);
3693 } else {
3694 to_insert = clipboard_text.as_str();
3695 entire_line = all_selections_were_entire_line;
3696 original_indent_column = first_selection_indent_column
3697 }
3698
3699 // If the corresponding selection was empty when this slice of the
3700 // clipboard text was written, then the entire line containing the
3701 // selection was copied. If this selection is also currently empty,
3702 // then paste the line before the current line of the buffer.
3703 let range = if selection.is_empty() && !line_mode && entire_line {
3704 let column = selection.start.to_point(&snapshot).column as usize;
3705 let line_start = selection.start - column;
3706 line_start..line_start
3707 } else {
3708 selection.range()
3709 };
3710
3711 edits.push((range, to_insert));
3712 original_indent_columns.extend(original_indent_column);
3713 }
3714 drop(snapshot);
3715
3716 buffer.edit(
3717 edits,
3718 Some(AutoindentMode::Block {
3719 original_indent_columns,
3720 }),
3721 cx,
3722 );
3723 });
3724
3725 let selections = this.selections.all::<usize>(cx);
3726 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3727 } else {
3728 this.insert(&clipboard_text, cx);
3729 }
3730 }
3731 });
3732 }
3733
3734 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3735 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3736 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3737 self.change_selections(None, cx, |s| {
3738 s.select_anchors(selections.to_vec());
3739 });
3740 }
3741 self.request_autoscroll(Autoscroll::Fit, cx);
3742 self.unmark_text(cx);
3743 cx.emit(Event::Edited);
3744 }
3745 }
3746
3747 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3748 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3749 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3750 {
3751 self.change_selections(None, cx, |s| {
3752 s.select_anchors(selections.to_vec());
3753 });
3754 }
3755 self.request_autoscroll(Autoscroll::Fit, cx);
3756 self.unmark_text(cx);
3757 cx.emit(Event::Edited);
3758 }
3759 }
3760
3761 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3762 self.buffer
3763 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3764 }
3765
3766 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3767 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3768 let line_mode = s.line_mode;
3769 s.move_with(|map, selection| {
3770 let cursor = if selection.is_empty() && !line_mode {
3771 movement::left(map, selection.start)
3772 } else {
3773 selection.start
3774 };
3775 selection.collapse_to(cursor, SelectionGoal::None);
3776 });
3777 })
3778 }
3779
3780 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3781 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3782 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3783 })
3784 }
3785
3786 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3787 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3788 let line_mode = s.line_mode;
3789 s.move_with(|map, selection| {
3790 let cursor = if selection.is_empty() && !line_mode {
3791 movement::right(map, selection.end)
3792 } else {
3793 selection.end
3794 };
3795 selection.collapse_to(cursor, SelectionGoal::None)
3796 });
3797 })
3798 }
3799
3800 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3801 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3802 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3803 })
3804 }
3805
3806 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3807 if self.take_rename(true, cx).is_some() {
3808 return;
3809 }
3810
3811 if let Some(context_menu) = self.context_menu.as_mut() {
3812 if context_menu.select_prev(cx) {
3813 return;
3814 }
3815 }
3816
3817 if matches!(self.mode, EditorMode::SingleLine) {
3818 cx.propagate_action();
3819 return;
3820 }
3821
3822 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3823 let line_mode = s.line_mode;
3824 s.move_with(|map, selection| {
3825 if !selection.is_empty() && !line_mode {
3826 selection.goal = SelectionGoal::None;
3827 }
3828 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3829 selection.collapse_to(cursor, goal);
3830 });
3831 })
3832 }
3833
3834 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3835 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3836 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3837 })
3838 }
3839
3840 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3841 self.take_rename(true, cx);
3842
3843 if let Some(context_menu) = self.context_menu.as_mut() {
3844 if context_menu.select_next(cx) {
3845 return;
3846 }
3847 }
3848
3849 if matches!(self.mode, EditorMode::SingleLine) {
3850 cx.propagate_action();
3851 return;
3852 }
3853
3854 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3855 let line_mode = s.line_mode;
3856 s.move_with(|map, selection| {
3857 if !selection.is_empty() && !line_mode {
3858 selection.goal = SelectionGoal::None;
3859 }
3860 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3861 selection.collapse_to(cursor, goal);
3862 });
3863 });
3864 }
3865
3866 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3867 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3868 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3869 });
3870 }
3871
3872 pub fn move_to_previous_word_start(
3873 &mut self,
3874 _: &MoveToPreviousWordStart,
3875 cx: &mut ViewContext<Self>,
3876 ) {
3877 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3878 s.move_cursors_with(|map, head, _| {
3879 (
3880 movement::previous_word_start(map, head),
3881 SelectionGoal::None,
3882 )
3883 });
3884 })
3885 }
3886
3887 pub fn move_to_previous_subword_start(
3888 &mut self,
3889 _: &MoveToPreviousSubwordStart,
3890 cx: &mut ViewContext<Self>,
3891 ) {
3892 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3893 s.move_cursors_with(|map, head, _| {
3894 (
3895 movement::previous_subword_start(map, head),
3896 SelectionGoal::None,
3897 )
3898 });
3899 })
3900 }
3901
3902 pub fn select_to_previous_word_start(
3903 &mut self,
3904 _: &SelectToPreviousWordStart,
3905 cx: &mut ViewContext<Self>,
3906 ) {
3907 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3908 s.move_heads_with(|map, head, _| {
3909 (
3910 movement::previous_word_start(map, head),
3911 SelectionGoal::None,
3912 )
3913 });
3914 })
3915 }
3916
3917 pub fn select_to_previous_subword_start(
3918 &mut self,
3919 _: &SelectToPreviousSubwordStart,
3920 cx: &mut ViewContext<Self>,
3921 ) {
3922 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3923 s.move_heads_with(|map, head, _| {
3924 (
3925 movement::previous_subword_start(map, head),
3926 SelectionGoal::None,
3927 )
3928 });
3929 })
3930 }
3931
3932 pub fn delete_to_previous_word_start(
3933 &mut self,
3934 _: &DeleteToPreviousWordStart,
3935 cx: &mut ViewContext<Self>,
3936 ) {
3937 self.transact(cx, |this, cx| {
3938 if !this.select_autoclose_pair(cx) {
3939 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3940 let line_mode = s.line_mode;
3941 s.move_with(|map, selection| {
3942 if selection.is_empty() && !line_mode {
3943 let cursor = movement::previous_word_start(map, selection.head());
3944 selection.set_head(cursor, SelectionGoal::None);
3945 }
3946 });
3947 });
3948 }
3949 this.insert("", cx);
3950 });
3951 }
3952
3953 pub fn delete_to_previous_subword_start(
3954 &mut self,
3955 _: &DeleteToPreviousSubwordStart,
3956 cx: &mut ViewContext<Self>,
3957 ) {
3958 self.transact(cx, |this, cx| {
3959 if !this.select_autoclose_pair(cx) {
3960 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3961 let line_mode = s.line_mode;
3962 s.move_with(|map, selection| {
3963 if selection.is_empty() && !line_mode {
3964 let cursor = movement::previous_subword_start(map, selection.head());
3965 selection.set_head(cursor, SelectionGoal::None);
3966 }
3967 });
3968 });
3969 }
3970 this.insert("", cx);
3971 });
3972 }
3973
3974 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3975 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3976 s.move_cursors_with(|map, head, _| {
3977 (movement::next_word_end(map, head), SelectionGoal::None)
3978 });
3979 })
3980 }
3981
3982 pub fn move_to_next_subword_end(
3983 &mut self,
3984 _: &MoveToNextSubwordEnd,
3985 cx: &mut ViewContext<Self>,
3986 ) {
3987 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3988 s.move_cursors_with(|map, head, _| {
3989 (movement::next_subword_end(map, head), SelectionGoal::None)
3990 });
3991 })
3992 }
3993
3994 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3995 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3996 s.move_heads_with(|map, head, _| {
3997 (movement::next_word_end(map, head), SelectionGoal::None)
3998 });
3999 })
4000 }
4001
4002 pub fn select_to_next_subword_end(
4003 &mut self,
4004 _: &SelectToNextSubwordEnd,
4005 cx: &mut ViewContext<Self>,
4006 ) {
4007 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4008 s.move_heads_with(|map, head, _| {
4009 (movement::next_subword_end(map, head), SelectionGoal::None)
4010 });
4011 })
4012 }
4013
4014 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4015 self.transact(cx, |this, cx| {
4016 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4017 let line_mode = s.line_mode;
4018 s.move_with(|map, selection| {
4019 if selection.is_empty() && !line_mode {
4020 let cursor = movement::next_word_end(map, selection.head());
4021 selection.set_head(cursor, SelectionGoal::None);
4022 }
4023 });
4024 });
4025 this.insert("", cx);
4026 });
4027 }
4028
4029 pub fn delete_to_next_subword_end(
4030 &mut self,
4031 _: &DeleteToNextSubwordEnd,
4032 cx: &mut ViewContext<Self>,
4033 ) {
4034 self.transact(cx, |this, cx| {
4035 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4036 s.move_with(|map, selection| {
4037 if selection.is_empty() {
4038 let cursor = movement::next_subword_end(map, selection.head());
4039 selection.set_head(cursor, SelectionGoal::None);
4040 }
4041 });
4042 });
4043 this.insert("", cx);
4044 });
4045 }
4046
4047 pub fn move_to_beginning_of_line(
4048 &mut self,
4049 _: &MoveToBeginningOfLine,
4050 cx: &mut ViewContext<Self>,
4051 ) {
4052 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4053 s.move_cursors_with(|map, head, _| {
4054 (
4055 movement::line_beginning(map, head, true),
4056 SelectionGoal::None,
4057 )
4058 });
4059 })
4060 }
4061
4062 pub fn select_to_beginning_of_line(
4063 &mut self,
4064 action: &SelectToBeginningOfLine,
4065 cx: &mut ViewContext<Self>,
4066 ) {
4067 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4068 s.move_heads_with(|map, head, _| {
4069 (
4070 movement::line_beginning(map, head, action.stop_at_soft_wraps),
4071 SelectionGoal::None,
4072 )
4073 });
4074 });
4075 }
4076
4077 pub fn delete_to_beginning_of_line(
4078 &mut self,
4079 _: &DeleteToBeginningOfLine,
4080 cx: &mut ViewContext<Self>,
4081 ) {
4082 self.transact(cx, |this, cx| {
4083 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4084 s.move_with(|_, selection| {
4085 selection.reversed = true;
4086 });
4087 });
4088
4089 this.select_to_beginning_of_line(
4090 &SelectToBeginningOfLine {
4091 stop_at_soft_wraps: false,
4092 },
4093 cx,
4094 );
4095 this.backspace(&Backspace, cx);
4096 });
4097 }
4098
4099 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4100 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4101 s.move_cursors_with(|map, head, _| {
4102 (movement::line_end(map, head, true), SelectionGoal::None)
4103 });
4104 })
4105 }
4106
4107 pub fn select_to_end_of_line(
4108 &mut self,
4109 action: &SelectToEndOfLine,
4110 cx: &mut ViewContext<Self>,
4111 ) {
4112 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4113 s.move_heads_with(|map, head, _| {
4114 (
4115 movement::line_end(map, head, action.stop_at_soft_wraps),
4116 SelectionGoal::None,
4117 )
4118 });
4119 })
4120 }
4121
4122 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4123 self.transact(cx, |this, cx| {
4124 this.select_to_end_of_line(
4125 &SelectToEndOfLine {
4126 stop_at_soft_wraps: false,
4127 },
4128 cx,
4129 );
4130 this.delete(&Delete, cx);
4131 });
4132 }
4133
4134 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4135 self.transact(cx, |this, cx| {
4136 this.select_to_end_of_line(
4137 &SelectToEndOfLine {
4138 stop_at_soft_wraps: false,
4139 },
4140 cx,
4141 );
4142 this.cut(&Cut, cx);
4143 });
4144 }
4145
4146 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4147 if matches!(self.mode, EditorMode::SingleLine) {
4148 cx.propagate_action();
4149 return;
4150 }
4151
4152 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4153 s.select_ranges(vec![0..0]);
4154 });
4155 }
4156
4157 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4158 let mut selection = self.selections.last::<Point>(cx);
4159 selection.set_head(Point::zero(), SelectionGoal::None);
4160
4161 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4162 s.select(vec![selection]);
4163 });
4164 }
4165
4166 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4167 if matches!(self.mode, EditorMode::SingleLine) {
4168 cx.propagate_action();
4169 return;
4170 }
4171
4172 let cursor = self.buffer.read(cx).read(cx).len();
4173 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4174 s.select_ranges(vec![cursor..cursor])
4175 });
4176 }
4177
4178 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4179 self.nav_history = nav_history;
4180 }
4181
4182 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4183 self.nav_history.as_ref()
4184 }
4185
4186 fn push_to_nav_history(
4187 &self,
4188 position: Anchor,
4189 new_position: Option<Point>,
4190 cx: &mut ViewContext<Self>,
4191 ) {
4192 if let Some(nav_history) = &self.nav_history {
4193 let buffer = self.buffer.read(cx).read(cx);
4194 let point = position.to_point(&buffer);
4195 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4196 drop(buffer);
4197
4198 if let Some(new_position) = new_position {
4199 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4200 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4201 return;
4202 }
4203 }
4204
4205 nav_history.push(
4206 Some(NavigationData {
4207 cursor_anchor: position,
4208 cursor_position: point,
4209 scroll_position: self.scroll_position,
4210 scroll_top_anchor: self.scroll_top_anchor.clone(),
4211 scroll_top_row,
4212 }),
4213 cx,
4214 );
4215 }
4216 }
4217
4218 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4219 let buffer = self.buffer.read(cx).snapshot(cx);
4220 let mut selection = self.selections.first::<usize>(cx);
4221 selection.set_head(buffer.len(), SelectionGoal::None);
4222 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4223 s.select(vec![selection]);
4224 });
4225 }
4226
4227 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4228 let end = self.buffer.read(cx).read(cx).len();
4229 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4230 s.select_ranges(vec![0..end]);
4231 });
4232 }
4233
4234 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4235 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4236 let mut selections = self.selections.all::<Point>(cx);
4237 let max_point = display_map.buffer_snapshot.max_point();
4238 for selection in &mut selections {
4239 let rows = selection.spanned_rows(true, &display_map);
4240 selection.start = Point::new(rows.start, 0);
4241 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4242 selection.reversed = false;
4243 }
4244 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4245 s.select(selections);
4246 });
4247 }
4248
4249 pub fn split_selection_into_lines(
4250 &mut self,
4251 _: &SplitSelectionIntoLines,
4252 cx: &mut ViewContext<Self>,
4253 ) {
4254 let mut to_unfold = Vec::new();
4255 let mut new_selection_ranges = Vec::new();
4256 {
4257 let selections = self.selections.all::<Point>(cx);
4258 let buffer = self.buffer.read(cx).read(cx);
4259 for selection in selections {
4260 for row in selection.start.row..selection.end.row {
4261 let cursor = Point::new(row, buffer.line_len(row));
4262 new_selection_ranges.push(cursor..cursor);
4263 }
4264 new_selection_ranges.push(selection.end..selection.end);
4265 to_unfold.push(selection.start..selection.end);
4266 }
4267 }
4268 self.unfold_ranges(to_unfold, true, cx);
4269 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4270 s.select_ranges(new_selection_ranges);
4271 });
4272 }
4273
4274 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4275 self.add_selection(true, cx);
4276 }
4277
4278 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4279 self.add_selection(false, cx);
4280 }
4281
4282 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4283 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4284 let mut selections = self.selections.all::<Point>(cx);
4285 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4286 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4287 let range = oldest_selection.display_range(&display_map).sorted();
4288 let columns = cmp::min(range.start.column(), range.end.column())
4289 ..cmp::max(range.start.column(), range.end.column());
4290
4291 selections.clear();
4292 let mut stack = Vec::new();
4293 for row in range.start.row()..=range.end.row() {
4294 if let Some(selection) = self.selections.build_columnar_selection(
4295 &display_map,
4296 row,
4297 &columns,
4298 oldest_selection.reversed,
4299 ) {
4300 stack.push(selection.id);
4301 selections.push(selection);
4302 }
4303 }
4304
4305 if above {
4306 stack.reverse();
4307 }
4308
4309 AddSelectionsState { above, stack }
4310 });
4311
4312 let last_added_selection = *state.stack.last().unwrap();
4313 let mut new_selections = Vec::new();
4314 if above == state.above {
4315 let end_row = if above {
4316 0
4317 } else {
4318 display_map.max_point().row()
4319 };
4320
4321 'outer: for selection in selections {
4322 if selection.id == last_added_selection {
4323 let range = selection.display_range(&display_map).sorted();
4324 debug_assert_eq!(range.start.row(), range.end.row());
4325 let mut row = range.start.row();
4326 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4327 {
4328 start..end
4329 } else {
4330 cmp::min(range.start.column(), range.end.column())
4331 ..cmp::max(range.start.column(), range.end.column())
4332 };
4333
4334 while row != end_row {
4335 if above {
4336 row -= 1;
4337 } else {
4338 row += 1;
4339 }
4340
4341 if let Some(new_selection) = self.selections.build_columnar_selection(
4342 &display_map,
4343 row,
4344 &columns,
4345 selection.reversed,
4346 ) {
4347 state.stack.push(new_selection.id);
4348 if above {
4349 new_selections.push(new_selection);
4350 new_selections.push(selection);
4351 } else {
4352 new_selections.push(selection);
4353 new_selections.push(new_selection);
4354 }
4355
4356 continue 'outer;
4357 }
4358 }
4359 }
4360
4361 new_selections.push(selection);
4362 }
4363 } else {
4364 new_selections = selections;
4365 new_selections.retain(|s| s.id != last_added_selection);
4366 state.stack.pop();
4367 }
4368
4369 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4370 s.select(new_selections);
4371 });
4372 if state.stack.len() > 1 {
4373 self.add_selections_state = Some(state);
4374 }
4375 }
4376
4377 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4378 self.push_to_selection_history();
4379 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4380 let buffer = &display_map.buffer_snapshot;
4381 let mut selections = self.selections.all::<usize>(cx);
4382 if let Some(mut select_next_state) = self.select_next_state.take() {
4383 let query = &select_next_state.query;
4384 if !select_next_state.done {
4385 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4386 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4387 let mut next_selected_range = None;
4388
4389 let bytes_after_last_selection =
4390 buffer.bytes_in_range(last_selection.end..buffer.len());
4391 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4392 let query_matches = query
4393 .stream_find_iter(bytes_after_last_selection)
4394 .map(|result| (last_selection.end, result))
4395 .chain(
4396 query
4397 .stream_find_iter(bytes_before_first_selection)
4398 .map(|result| (0, result)),
4399 );
4400 for (start_offset, query_match) in query_matches {
4401 let query_match = query_match.unwrap(); // can only fail due to I/O
4402 let offset_range =
4403 start_offset + query_match.start()..start_offset + query_match.end();
4404 let display_range = offset_range.start.to_display_point(&display_map)
4405 ..offset_range.end.to_display_point(&display_map);
4406
4407 if !select_next_state.wordwise
4408 || (!movement::is_inside_word(&display_map, display_range.start)
4409 && !movement::is_inside_word(&display_map, display_range.end))
4410 {
4411 next_selected_range = Some(offset_range);
4412 break;
4413 }
4414 }
4415
4416 if let Some(next_selected_range) = next_selected_range {
4417 self.unfold_ranges([next_selected_range.clone()], false, cx);
4418 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4419 if action.replace_newest {
4420 s.delete(s.newest_anchor().id);
4421 }
4422 s.insert_range(next_selected_range);
4423 });
4424 } else {
4425 select_next_state.done = true;
4426 }
4427 }
4428
4429 self.select_next_state = Some(select_next_state);
4430 } else if selections.len() == 1 {
4431 let selection = selections.last_mut().unwrap();
4432 if selection.start == selection.end {
4433 let word_range = movement::surrounding_word(
4434 &display_map,
4435 selection.start.to_display_point(&display_map),
4436 );
4437 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4438 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4439 selection.goal = SelectionGoal::None;
4440 selection.reversed = false;
4441
4442 let query = buffer
4443 .text_for_range(selection.start..selection.end)
4444 .collect::<String>();
4445 let select_state = SelectNextState {
4446 query: AhoCorasick::new_auto_configured(&[query]),
4447 wordwise: true,
4448 done: false,
4449 };
4450 self.unfold_ranges([selection.start..selection.end], false, cx);
4451 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4452 s.select(selections);
4453 });
4454 self.select_next_state = Some(select_state);
4455 } else {
4456 let query = buffer
4457 .text_for_range(selection.start..selection.end)
4458 .collect::<String>();
4459 self.select_next_state = Some(SelectNextState {
4460 query: AhoCorasick::new_auto_configured(&[query]),
4461 wordwise: false,
4462 done: false,
4463 });
4464 self.select_next(action, cx);
4465 }
4466 }
4467 }
4468
4469 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4470 self.transact(cx, |this, cx| {
4471 let mut selections = this.selections.all::<Point>(cx);
4472 let mut all_selection_lines_are_comments = true;
4473 let mut edit_ranges = Vec::new();
4474 let mut last_toggled_row = None;
4475 this.buffer.update(cx, |buffer, cx| {
4476 // TODO: Handle selections that cross excerpts
4477 for selection in &mut selections {
4478 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4479 // as that portion won't be used for detecting if a line is a comment.
4480 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4481 .language_at(selection.start, cx)
4482 .and_then(|l| l.line_comment_prefix())
4483 {
4484 prefix.into()
4485 } else {
4486 return;
4487 };
4488 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4489 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4490 edit_ranges.clear();
4491 let snapshot = buffer.snapshot(cx);
4492
4493 let end_row =
4494 if selection.end.row > selection.start.row && selection.end.column == 0 {
4495 selection.end.row
4496 } else {
4497 selection.end.row + 1
4498 };
4499
4500 for row in selection.start.row..end_row {
4501 // If multiple selections contain a given row, avoid processing that
4502 // row more than once.
4503 if last_toggled_row == Some(row) {
4504 continue;
4505 } else {
4506 last_toggled_row = Some(row);
4507 }
4508
4509 if snapshot.is_line_blank(row) {
4510 continue;
4511 }
4512
4513 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4514 let mut line_bytes = snapshot
4515 .bytes_in_range(start..snapshot.max_point())
4516 .flatten()
4517 .copied();
4518
4519 // If this line currently begins with the line comment prefix, then record
4520 // the range containing the prefix.
4521 if all_selection_lines_are_comments
4522 && line_bytes
4523 .by_ref()
4524 .take(comment_prefix.len())
4525 .eq(comment_prefix.bytes())
4526 {
4527 // Include any whitespace that matches the comment prefix.
4528 let matching_whitespace_len = line_bytes
4529 .zip(comment_prefix_whitespace.bytes())
4530 .take_while(|(a, b)| a == b)
4531 .count()
4532 as u32;
4533 let end = Point::new(
4534 row,
4535 start.column
4536 + comment_prefix.len() as u32
4537 + matching_whitespace_len,
4538 );
4539 edit_ranges.push(start..end);
4540 }
4541 // If this line does not begin with the line comment prefix, then record
4542 // the position where the prefix should be inserted.
4543 else {
4544 all_selection_lines_are_comments = false;
4545 edit_ranges.push(start..start);
4546 }
4547 }
4548
4549 if !edit_ranges.is_empty() {
4550 if all_selection_lines_are_comments {
4551 let empty_str: Arc<str> = "".into();
4552 buffer.edit(
4553 edit_ranges
4554 .iter()
4555 .cloned()
4556 .map(|range| (range, empty_str.clone())),
4557 None,
4558 cx,
4559 );
4560 } else {
4561 let min_column =
4562 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4563 let edits = edit_ranges.iter().map(|range| {
4564 let position = Point::new(range.start.row, min_column);
4565 (position..position, full_comment_prefix.clone())
4566 });
4567 buffer.edit(edits, None, cx);
4568 }
4569 }
4570 }
4571 });
4572
4573 let selections = this.selections.all::<usize>(cx);
4574 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4575 });
4576 }
4577
4578 pub fn select_larger_syntax_node(
4579 &mut self,
4580 _: &SelectLargerSyntaxNode,
4581 cx: &mut ViewContext<Self>,
4582 ) {
4583 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4584 let buffer = self.buffer.read(cx).snapshot(cx);
4585 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4586
4587 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4588 let mut selected_larger_node = false;
4589 let new_selections = old_selections
4590 .iter()
4591 .map(|selection| {
4592 let old_range = selection.start..selection.end;
4593 let mut new_range = old_range.clone();
4594 while let Some(containing_range) =
4595 buffer.range_for_syntax_ancestor(new_range.clone())
4596 {
4597 new_range = containing_range;
4598 if !display_map.intersects_fold(new_range.start)
4599 && !display_map.intersects_fold(new_range.end)
4600 {
4601 break;
4602 }
4603 }
4604
4605 selected_larger_node |= new_range != old_range;
4606 Selection {
4607 id: selection.id,
4608 start: new_range.start,
4609 end: new_range.end,
4610 goal: SelectionGoal::None,
4611 reversed: selection.reversed,
4612 }
4613 })
4614 .collect::<Vec<_>>();
4615
4616 if selected_larger_node {
4617 stack.push(old_selections);
4618 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4619 s.select(new_selections);
4620 });
4621 }
4622 self.select_larger_syntax_node_stack = stack;
4623 }
4624
4625 pub fn select_smaller_syntax_node(
4626 &mut self,
4627 _: &SelectSmallerSyntaxNode,
4628 cx: &mut ViewContext<Self>,
4629 ) {
4630 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4631 if let Some(selections) = stack.pop() {
4632 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4633 s.select(selections.to_vec());
4634 });
4635 }
4636 self.select_larger_syntax_node_stack = stack;
4637 }
4638
4639 pub fn move_to_enclosing_bracket(
4640 &mut self,
4641 _: &MoveToEnclosingBracket,
4642 cx: &mut ViewContext<Self>,
4643 ) {
4644 let buffer = self.buffer.read(cx).snapshot(cx);
4645 let mut selections = self.selections.all::<usize>(cx);
4646 for selection in &mut selections {
4647 if let Some((open_range, close_range)) =
4648 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4649 {
4650 let close_range = close_range.to_inclusive();
4651 let destination = if close_range.contains(&selection.start)
4652 && close_range.contains(&selection.end)
4653 {
4654 open_range.end
4655 } else {
4656 *close_range.start()
4657 };
4658 selection.start = destination;
4659 selection.end = destination;
4660 }
4661 }
4662
4663 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4664 s.select(selections);
4665 });
4666 }
4667
4668 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4669 self.end_selection(cx);
4670 self.selection_history.mode = SelectionHistoryMode::Undoing;
4671 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4672 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4673 self.select_next_state = entry.select_next_state;
4674 self.add_selections_state = entry.add_selections_state;
4675 self.request_autoscroll(Autoscroll::Newest, cx);
4676 }
4677 self.selection_history.mode = SelectionHistoryMode::Normal;
4678 }
4679
4680 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4681 self.end_selection(cx);
4682 self.selection_history.mode = SelectionHistoryMode::Redoing;
4683 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4684 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4685 self.select_next_state = entry.select_next_state;
4686 self.add_selections_state = entry.add_selections_state;
4687 self.request_autoscroll(Autoscroll::Newest, cx);
4688 }
4689 self.selection_history.mode = SelectionHistoryMode::Normal;
4690 }
4691
4692 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
4693 self.go_to_diagnostic_impl(Direction::Next, cx)
4694 }
4695
4696 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4697 self.go_to_diagnostic_impl(Direction::Prev, cx)
4698 }
4699
4700 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4701 let buffer = self.buffer.read(cx).snapshot(cx);
4702 let selection = self.selections.newest::<usize>(cx);
4703
4704 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
4705 if direction == Direction::Next {
4706 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
4707 let (group_id, jump_to) = popover.activation_info();
4708 if self.activate_diagnostics(group_id, cx) {
4709 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4710 let mut new_selection = s.newest_anchor().clone();
4711 new_selection.collapse_to(jump_to, SelectionGoal::None);
4712 s.select_anchors(vec![new_selection.clone()]);
4713 });
4714 }
4715 return;
4716 }
4717 }
4718
4719 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4720 active_diagnostics
4721 .primary_range
4722 .to_offset(&buffer)
4723 .to_inclusive()
4724 });
4725 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4726 if active_primary_range.contains(&selection.head()) {
4727 *active_primary_range.end()
4728 } else {
4729 selection.head()
4730 }
4731 } else {
4732 selection.head()
4733 };
4734
4735 loop {
4736 let mut diagnostics = if direction == Direction::Prev {
4737 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4738 } else {
4739 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4740 };
4741 let group = diagnostics.find_map(|entry| {
4742 if entry.diagnostic.is_primary
4743 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4744 && !entry.range.is_empty()
4745 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4746 {
4747 Some((entry.range, entry.diagnostic.group_id))
4748 } else {
4749 None
4750 }
4751 });
4752
4753 if let Some((primary_range, group_id)) = group {
4754 if self.activate_diagnostics(group_id, cx) {
4755 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4756 s.select(vec![Selection {
4757 id: selection.id,
4758 start: primary_range.start,
4759 end: primary_range.start,
4760 reversed: false,
4761 goal: SelectionGoal::None,
4762 }]);
4763 });
4764 }
4765 break;
4766 } else {
4767 // Cycle around to the start of the buffer, potentially moving back to the start of
4768 // the currently active diagnostic.
4769 active_primary_range.take();
4770 if direction == Direction::Prev {
4771 if search_start == buffer.len() {
4772 break;
4773 } else {
4774 search_start = buffer.len();
4775 }
4776 } else {
4777 if search_start == 0 {
4778 break;
4779 } else {
4780 search_start = 0;
4781 }
4782 }
4783 }
4784 }
4785 }
4786
4787 pub fn go_to_definition(
4788 workspace: &mut Workspace,
4789 _: &GoToDefinition,
4790 cx: &mut ViewContext<Workspace>,
4791 ) {
4792 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
4793 }
4794
4795 pub fn go_to_type_definition(
4796 workspace: &mut Workspace,
4797 _: &GoToTypeDefinition,
4798 cx: &mut ViewContext<Workspace>,
4799 ) {
4800 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
4801 }
4802
4803 fn go_to_definition_of_kind(
4804 kind: GotoDefinitionKind,
4805 workspace: &mut Workspace,
4806 cx: &mut ViewContext<Workspace>,
4807 ) {
4808 let active_item = workspace.active_item(cx);
4809 let editor_handle = if let Some(editor) = active_item
4810 .as_ref()
4811 .and_then(|item| item.act_as::<Self>(cx))
4812 {
4813 editor
4814 } else {
4815 return;
4816 };
4817
4818 let editor = editor_handle.read(cx);
4819 let buffer = editor.buffer.read(cx);
4820 let head = editor.selections.newest::<usize>(cx).head();
4821 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4822 text_anchor
4823 } else {
4824 return;
4825 };
4826
4827 let project = workspace.project().clone();
4828 let definitions = project.update(cx, |project, cx| match kind {
4829 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
4830 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
4831 });
4832
4833 cx.spawn(|workspace, mut cx| async move {
4834 let definitions = definitions.await?;
4835 workspace.update(&mut cx, |workspace, cx| {
4836 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4837 });
4838
4839 Ok::<(), anyhow::Error>(())
4840 })
4841 .detach_and_log_err(cx);
4842 }
4843
4844 pub fn navigate_to_definitions(
4845 workspace: &mut Workspace,
4846 editor_handle: ViewHandle<Editor>,
4847 definitions: Vec<LocationLink>,
4848 cx: &mut ViewContext<Workspace>,
4849 ) {
4850 let pane = workspace.active_pane().clone();
4851 for definition in definitions {
4852 let range = definition
4853 .target
4854 .range
4855 .to_offset(definition.target.buffer.read(cx));
4856
4857 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4858 target_editor_handle.update(cx, |target_editor, cx| {
4859 // When selecting a definition in a different buffer, disable the nav history
4860 // to avoid creating a history entry at the previous cursor location.
4861 if editor_handle != target_editor_handle {
4862 pane.update(cx, |pane, _| pane.disable_history());
4863 }
4864 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4865 s.select_ranges([range]);
4866 });
4867
4868 pane.update(cx, |pane, _| pane.enable_history());
4869 });
4870 }
4871 }
4872
4873 pub fn find_all_references(
4874 workspace: &mut Workspace,
4875 _: &FindAllReferences,
4876 cx: &mut ViewContext<Workspace>,
4877 ) -> Option<Task<Result<()>>> {
4878 let active_item = workspace.active_item(cx)?;
4879 let editor_handle = active_item.act_as::<Self>(cx)?;
4880
4881 let editor = editor_handle.read(cx);
4882 let buffer = editor.buffer.read(cx);
4883 let head = editor.selections.newest::<usize>(cx).head();
4884 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4885 let replica_id = editor.replica_id(cx);
4886
4887 let project = workspace.project().clone();
4888 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4889 Some(cx.spawn(|workspace, mut cx| async move {
4890 let mut locations = references.await?;
4891 if locations.is_empty() {
4892 return Ok(());
4893 }
4894
4895 locations.sort_by_key(|location| location.buffer.id());
4896 let mut locations = locations.into_iter().peekable();
4897 let mut ranges_to_highlight = Vec::new();
4898
4899 let excerpt_buffer = cx.add_model(|cx| {
4900 let mut symbol_name = None;
4901 let mut multibuffer = MultiBuffer::new(replica_id);
4902 while let Some(location) = locations.next() {
4903 let buffer = location.buffer.read(cx);
4904 let mut ranges_for_buffer = Vec::new();
4905 let range = location.range.to_offset(buffer);
4906 ranges_for_buffer.push(range.clone());
4907 if symbol_name.is_none() {
4908 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4909 }
4910
4911 while let Some(next_location) = locations.peek() {
4912 if next_location.buffer == location.buffer {
4913 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4914 locations.next();
4915 } else {
4916 break;
4917 }
4918 }
4919
4920 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4921 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4922 location.buffer.clone(),
4923 ranges_for_buffer,
4924 1,
4925 cx,
4926 ));
4927 }
4928 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4929 });
4930
4931 workspace.update(&mut cx, |workspace, cx| {
4932 let editor =
4933 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4934 editor.update(cx, |editor, cx| {
4935 editor.highlight_background::<Self>(
4936 ranges_to_highlight,
4937 |theme| theme.editor.highlighted_line_background,
4938 cx,
4939 );
4940 });
4941 workspace.add_item(Box::new(editor), cx);
4942 });
4943
4944 Ok(())
4945 }))
4946 }
4947
4948 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4949 use language::ToOffset as _;
4950
4951 let project = self.project.clone()?;
4952 let selection = self.selections.newest_anchor().clone();
4953 let (cursor_buffer, cursor_buffer_position) = self
4954 .buffer
4955 .read(cx)
4956 .text_anchor_for_position(selection.head(), cx)?;
4957 let (tail_buffer, _) = self
4958 .buffer
4959 .read(cx)
4960 .text_anchor_for_position(selection.tail(), cx)?;
4961 if tail_buffer != cursor_buffer {
4962 return None;
4963 }
4964
4965 let snapshot = cursor_buffer.read(cx).snapshot();
4966 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4967 let prepare_rename = project.update(cx, |project, cx| {
4968 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4969 });
4970
4971 Some(cx.spawn(|this, mut cx| async move {
4972 let rename_range = if let Some(range) = prepare_rename.await? {
4973 Some(range)
4974 } else {
4975 this.read_with(&cx, |this, cx| {
4976 let buffer = this.buffer.read(cx).snapshot(cx);
4977 let mut buffer_highlights = this
4978 .document_highlights_for_position(selection.head(), &buffer)
4979 .filter(|highlight| {
4980 highlight.start.excerpt_id() == selection.head().excerpt_id()
4981 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4982 });
4983 buffer_highlights
4984 .next()
4985 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4986 })
4987 };
4988 if let Some(rename_range) = rename_range {
4989 let rename_buffer_range = rename_range.to_offset(&snapshot);
4990 let cursor_offset_in_rename_range =
4991 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4992
4993 this.update(&mut cx, |this, cx| {
4994 this.take_rename(false, cx);
4995 let style = this.style(cx);
4996 let buffer = this.buffer.read(cx).read(cx);
4997 let cursor_offset = selection.head().to_offset(&buffer);
4998 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4999 let rename_end = rename_start + rename_buffer_range.len();
5000 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5001 let mut old_highlight_id = None;
5002 let old_name: Arc<str> = buffer
5003 .chunks(rename_start..rename_end, true)
5004 .map(|chunk| {
5005 if old_highlight_id.is_none() {
5006 old_highlight_id = chunk.syntax_highlight_id;
5007 }
5008 chunk.text
5009 })
5010 .collect::<String>()
5011 .into();
5012
5013 drop(buffer);
5014
5015 // Position the selection in the rename editor so that it matches the current selection.
5016 this.show_local_selections = false;
5017 let rename_editor = cx.add_view(|cx| {
5018 let mut editor = Editor::single_line(None, cx);
5019 if let Some(old_highlight_id) = old_highlight_id {
5020 editor.override_text_style =
5021 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5022 }
5023 editor.buffer.update(cx, |buffer, cx| {
5024 buffer.edit([(0..0, old_name.clone())], None, cx)
5025 });
5026 editor.select_all(&SelectAll, cx);
5027 editor
5028 });
5029
5030 let ranges = this
5031 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5032 .into_iter()
5033 .flat_map(|(_, ranges)| ranges)
5034 .chain(
5035 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5036 .into_iter()
5037 .flat_map(|(_, ranges)| ranges),
5038 )
5039 .collect();
5040
5041 this.highlight_text::<Rename>(
5042 ranges,
5043 HighlightStyle {
5044 fade_out: Some(style.rename_fade),
5045 ..Default::default()
5046 },
5047 cx,
5048 );
5049 cx.focus(&rename_editor);
5050 let block_id = this.insert_blocks(
5051 [BlockProperties {
5052 style: BlockStyle::Flex,
5053 position: range.start.clone(),
5054 height: 1,
5055 render: Arc::new({
5056 let editor = rename_editor.clone();
5057 move |cx: &mut BlockContext| {
5058 ChildView::new(editor.clone())
5059 .contained()
5060 .with_padding_left(cx.anchor_x)
5061 .boxed()
5062 }
5063 }),
5064 disposition: BlockDisposition::Below,
5065 }],
5066 cx,
5067 )[0];
5068 this.pending_rename = Some(RenameState {
5069 range,
5070 old_name,
5071 editor: rename_editor,
5072 block_id,
5073 });
5074 });
5075 }
5076
5077 Ok(())
5078 }))
5079 }
5080
5081 pub fn confirm_rename(
5082 workspace: &mut Workspace,
5083 _: &ConfirmRename,
5084 cx: &mut ViewContext<Workspace>,
5085 ) -> Option<Task<Result<()>>> {
5086 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5087
5088 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5089 let rename = editor.take_rename(false, cx)?;
5090 let buffer = editor.buffer.read(cx);
5091 let (start_buffer, start) =
5092 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5093 let (end_buffer, end) =
5094 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5095 if start_buffer == end_buffer {
5096 let new_name = rename.editor.read(cx).text(cx);
5097 Some((start_buffer, start..end, rename.old_name, new_name))
5098 } else {
5099 None
5100 }
5101 })?;
5102
5103 let rename = workspace.project().clone().update(cx, |project, cx| {
5104 project.perform_rename(
5105 buffer.clone(),
5106 range.start.clone(),
5107 new_name.clone(),
5108 true,
5109 cx,
5110 )
5111 });
5112
5113 Some(cx.spawn(|workspace, mut cx| async move {
5114 let project_transaction = rename.await?;
5115 Self::open_project_transaction(
5116 editor.clone(),
5117 workspace,
5118 project_transaction,
5119 format!("Rename: {} → {}", old_name, new_name),
5120 cx.clone(),
5121 )
5122 .await?;
5123
5124 editor.update(&mut cx, |editor, cx| {
5125 editor.refresh_document_highlights(cx);
5126 });
5127 Ok(())
5128 }))
5129 }
5130
5131 fn take_rename(
5132 &mut self,
5133 moving_cursor: bool,
5134 cx: &mut ViewContext<Self>,
5135 ) -> Option<RenameState> {
5136 let rename = self.pending_rename.take()?;
5137 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5138 self.clear_text_highlights::<Rename>(cx);
5139 self.show_local_selections = true;
5140
5141 if moving_cursor {
5142 let rename_editor = rename.editor.read(cx);
5143 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5144
5145 // Update the selection to match the position of the selection inside
5146 // the rename editor.
5147 let snapshot = self.buffer.read(cx).read(cx);
5148 let rename_range = rename.range.to_offset(&snapshot);
5149 let cursor_in_editor = snapshot
5150 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5151 .min(rename_range.end);
5152 drop(snapshot);
5153
5154 self.change_selections(None, cx, |s| {
5155 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5156 });
5157 } else {
5158 self.refresh_document_highlights(cx);
5159 }
5160
5161 Some(rename)
5162 }
5163
5164 #[cfg(any(test, feature = "test-support"))]
5165 pub fn pending_rename(&self) -> Option<&RenameState> {
5166 self.pending_rename.as_ref()
5167 }
5168
5169 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5170 if let Some(project) = self.project.clone() {
5171 self.buffer.update(cx, |multi_buffer, cx| {
5172 project.update(cx, |project, cx| {
5173 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5174 });
5175 })
5176 }
5177 }
5178
5179 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5180 cx.show_character_palette();
5181 }
5182
5183 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5184 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5185 let buffer = self.buffer.read(cx).snapshot(cx);
5186 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5187 let is_valid = buffer
5188 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5189 .any(|entry| {
5190 entry.diagnostic.is_primary
5191 && !entry.range.is_empty()
5192 && entry.range.start == primary_range_start
5193 && entry.diagnostic.message == active_diagnostics.primary_message
5194 });
5195
5196 if is_valid != active_diagnostics.is_valid {
5197 active_diagnostics.is_valid = is_valid;
5198 let mut new_styles = HashMap::default();
5199 for (block_id, diagnostic) in &active_diagnostics.blocks {
5200 new_styles.insert(
5201 *block_id,
5202 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5203 );
5204 }
5205 self.display_map
5206 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5207 }
5208 }
5209 }
5210
5211 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5212 self.dismiss_diagnostics(cx);
5213 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5214 let buffer = self.buffer.read(cx).snapshot(cx);
5215
5216 let mut primary_range = None;
5217 let mut primary_message = None;
5218 let mut group_end = Point::zero();
5219 let diagnostic_group = buffer
5220 .diagnostic_group::<Point>(group_id)
5221 .map(|entry| {
5222 if entry.range.end > group_end {
5223 group_end = entry.range.end;
5224 }
5225 if entry.diagnostic.is_primary {
5226 primary_range = Some(entry.range.clone());
5227 primary_message = Some(entry.diagnostic.message.clone());
5228 }
5229 entry
5230 })
5231 .collect::<Vec<_>>();
5232 let primary_range = primary_range?;
5233 let primary_message = primary_message?;
5234 let primary_range =
5235 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5236
5237 let blocks = display_map
5238 .insert_blocks(
5239 diagnostic_group.iter().map(|entry| {
5240 let diagnostic = entry.diagnostic.clone();
5241 let message_height = diagnostic.message.lines().count() as u8;
5242 BlockProperties {
5243 style: BlockStyle::Fixed,
5244 position: buffer.anchor_after(entry.range.start),
5245 height: message_height,
5246 render: diagnostic_block_renderer(diagnostic, true),
5247 disposition: BlockDisposition::Below,
5248 }
5249 }),
5250 cx,
5251 )
5252 .into_iter()
5253 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5254 .collect();
5255
5256 Some(ActiveDiagnosticGroup {
5257 primary_range,
5258 primary_message,
5259 blocks,
5260 is_valid: true,
5261 })
5262 });
5263 self.active_diagnostics.is_some()
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_ranges, marked_text_ranges_by, parse_marked_text, sample_text,
6649 TextRangeMarker,
6650 },
6651 };
6652 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6653
6654 #[gpui::test]
6655 fn test_edit_events(cx: &mut MutableAppContext) {
6656 cx.set_global(Settings::test(cx));
6657 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6658
6659 let events = Rc::new(RefCell::new(Vec::new()));
6660 let (_, editor1) = cx.add_window(Default::default(), {
6661 let events = events.clone();
6662 |cx| {
6663 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6664 if matches!(
6665 event,
6666 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6667 ) {
6668 events.borrow_mut().push(("editor1", *event));
6669 }
6670 })
6671 .detach();
6672 Editor::for_buffer(buffer.clone(), None, cx)
6673 }
6674 });
6675 let (_, editor2) = cx.add_window(Default::default(), {
6676 let events = events.clone();
6677 |cx| {
6678 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6679 if matches!(
6680 event,
6681 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6682 ) {
6683 events.borrow_mut().push(("editor2", *event));
6684 }
6685 })
6686 .detach();
6687 Editor::for_buffer(buffer.clone(), None, cx)
6688 }
6689 });
6690 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6691
6692 // Mutating editor 1 will emit an `Edited` event only for that editor.
6693 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6694 assert_eq!(
6695 mem::take(&mut *events.borrow_mut()),
6696 [
6697 ("editor1", Event::Edited),
6698 ("editor1", Event::BufferEdited),
6699 ("editor2", Event::BufferEdited),
6700 ("editor1", Event::DirtyChanged),
6701 ("editor2", Event::DirtyChanged)
6702 ]
6703 );
6704
6705 // Mutating editor 2 will emit an `Edited` event only for that editor.
6706 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6707 assert_eq!(
6708 mem::take(&mut *events.borrow_mut()),
6709 [
6710 ("editor2", Event::Edited),
6711 ("editor1", Event::BufferEdited),
6712 ("editor2", Event::BufferEdited),
6713 ]
6714 );
6715
6716 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6717 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6718 assert_eq!(
6719 mem::take(&mut *events.borrow_mut()),
6720 [
6721 ("editor1", Event::Edited),
6722 ("editor1", Event::BufferEdited),
6723 ("editor2", Event::BufferEdited),
6724 ("editor1", Event::DirtyChanged),
6725 ("editor2", Event::DirtyChanged),
6726 ]
6727 );
6728
6729 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6730 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6731 assert_eq!(
6732 mem::take(&mut *events.borrow_mut()),
6733 [
6734 ("editor1", Event::Edited),
6735 ("editor1", Event::BufferEdited),
6736 ("editor2", Event::BufferEdited),
6737 ("editor1", Event::DirtyChanged),
6738 ("editor2", Event::DirtyChanged),
6739 ]
6740 );
6741
6742 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6743 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6744 assert_eq!(
6745 mem::take(&mut *events.borrow_mut()),
6746 [
6747 ("editor2", Event::Edited),
6748 ("editor1", Event::BufferEdited),
6749 ("editor2", Event::BufferEdited),
6750 ("editor1", Event::DirtyChanged),
6751 ("editor2", Event::DirtyChanged),
6752 ]
6753 );
6754
6755 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6756 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6757 assert_eq!(
6758 mem::take(&mut *events.borrow_mut()),
6759 [
6760 ("editor2", Event::Edited),
6761 ("editor1", Event::BufferEdited),
6762 ("editor2", Event::BufferEdited),
6763 ("editor1", Event::DirtyChanged),
6764 ("editor2", Event::DirtyChanged),
6765 ]
6766 );
6767
6768 // No event is emitted when the mutation is a no-op.
6769 editor2.update(cx, |editor, cx| {
6770 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6771
6772 editor.backspace(&Backspace, cx);
6773 });
6774 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6775 }
6776
6777 #[gpui::test]
6778 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6779 cx.set_global(Settings::test(cx));
6780 let mut now = Instant::now();
6781 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6782 let group_interval = buffer.read(cx).transaction_group_interval();
6783 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6784 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6785
6786 editor.update(cx, |editor, cx| {
6787 editor.start_transaction_at(now, cx);
6788 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6789
6790 editor.insert("cd", cx);
6791 editor.end_transaction_at(now, cx);
6792 assert_eq!(editor.text(cx), "12cd56");
6793 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6794
6795 editor.start_transaction_at(now, cx);
6796 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6797 editor.insert("e", cx);
6798 editor.end_transaction_at(now, cx);
6799 assert_eq!(editor.text(cx), "12cde6");
6800 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6801
6802 now += group_interval + Duration::from_millis(1);
6803 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6804
6805 // Simulate an edit in another editor
6806 buffer.update(cx, |buffer, cx| {
6807 buffer.start_transaction_at(now, cx);
6808 buffer.edit([(0..1, "a")], None, cx);
6809 buffer.edit([(1..1, "b")], None, cx);
6810 buffer.end_transaction_at(now, cx);
6811 });
6812
6813 assert_eq!(editor.text(cx), "ab2cde6");
6814 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6815
6816 // Last transaction happened past the group interval in a different editor.
6817 // Undo it individually and don't restore selections.
6818 editor.undo(&Undo, cx);
6819 assert_eq!(editor.text(cx), "12cde6");
6820 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6821
6822 // First two transactions happened within the group interval in this editor.
6823 // Undo them together and restore selections.
6824 editor.undo(&Undo, cx);
6825 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6826 assert_eq!(editor.text(cx), "123456");
6827 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6828
6829 // Redo the first two transactions together.
6830 editor.redo(&Redo, cx);
6831 assert_eq!(editor.text(cx), "12cde6");
6832 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6833
6834 // Redo the last transaction on its own.
6835 editor.redo(&Redo, cx);
6836 assert_eq!(editor.text(cx), "ab2cde6");
6837 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6838
6839 // Test empty transactions.
6840 editor.start_transaction_at(now, cx);
6841 editor.end_transaction_at(now, cx);
6842 editor.undo(&Undo, cx);
6843 assert_eq!(editor.text(cx), "12cde6");
6844 });
6845 }
6846
6847 #[gpui::test]
6848 fn test_ime_composition(cx: &mut MutableAppContext) {
6849 cx.set_global(Settings::test(cx));
6850 let buffer = cx.add_model(|cx| {
6851 let mut buffer = language::Buffer::new(0, "abcde", cx);
6852 // Ensure automatic grouping doesn't occur.
6853 buffer.set_group_interval(Duration::ZERO);
6854 buffer
6855 });
6856
6857 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6858 cx.add_window(Default::default(), |cx| {
6859 let mut editor = build_editor(buffer.clone(), cx);
6860
6861 // Start a new IME composition.
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 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
6865 assert_eq!(editor.text(cx), "äbcde");
6866 assert_eq!(
6867 editor.marked_text_ranges(cx),
6868 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6869 );
6870
6871 // Finalize IME composition.
6872 editor.replace_text_in_range(None, "ā", cx);
6873 assert_eq!(editor.text(cx), "ābcde");
6874 assert_eq!(editor.marked_text_ranges(cx), None);
6875
6876 // IME composition edits are grouped and are undone/redone at once.
6877 editor.undo(&Default::default(), cx);
6878 assert_eq!(editor.text(cx), "abcde");
6879 assert_eq!(editor.marked_text_ranges(cx), None);
6880 editor.redo(&Default::default(), cx);
6881 assert_eq!(editor.text(cx), "ābcde");
6882 assert_eq!(editor.marked_text_ranges(cx), None);
6883
6884 // Start a new IME composition.
6885 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6886 assert_eq!(
6887 editor.marked_text_ranges(cx),
6888 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6889 );
6890
6891 // Undoing during an IME composition cancels it.
6892 editor.undo(&Default::default(), cx);
6893 assert_eq!(editor.text(cx), "ābcde");
6894 assert_eq!(editor.marked_text_ranges(cx), None);
6895
6896 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
6897 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
6898 assert_eq!(editor.text(cx), "ābcdè");
6899 assert_eq!(
6900 editor.marked_text_ranges(cx),
6901 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
6902 );
6903
6904 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
6905 editor.replace_text_in_range(Some(4..999), "ę", cx);
6906 assert_eq!(editor.text(cx), "ābcdę");
6907 assert_eq!(editor.marked_text_ranges(cx), None);
6908
6909 // Start a new IME composition with multiple cursors.
6910 editor.change_selections(None, cx, |s| {
6911 s.select_ranges([
6912 OffsetUtf16(1)..OffsetUtf16(1),
6913 OffsetUtf16(3)..OffsetUtf16(3),
6914 OffsetUtf16(5)..OffsetUtf16(5),
6915 ])
6916 });
6917 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
6918 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
6919 assert_eq!(
6920 editor.marked_text_ranges(cx),
6921 Some(vec![
6922 OffsetUtf16(0)..OffsetUtf16(3),
6923 OffsetUtf16(4)..OffsetUtf16(7),
6924 OffsetUtf16(8)..OffsetUtf16(11)
6925 ])
6926 );
6927
6928 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
6929 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
6930 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
6931 assert_eq!(
6932 editor.marked_text_ranges(cx),
6933 Some(vec![
6934 OffsetUtf16(1)..OffsetUtf16(2),
6935 OffsetUtf16(5)..OffsetUtf16(6),
6936 OffsetUtf16(9)..OffsetUtf16(10)
6937 ])
6938 );
6939
6940 // Finalize IME composition with multiple cursors.
6941 editor.replace_text_in_range(Some(9..10), "2", cx);
6942 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
6943 assert_eq!(editor.marked_text_ranges(cx), None);
6944
6945 editor
6946 });
6947 }
6948
6949 #[gpui::test]
6950 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6951 cx.set_global(Settings::test(cx));
6952
6953 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6954 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6955 editor.update(cx, |view, cx| {
6956 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6957 });
6958 assert_eq!(
6959 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6960 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6961 );
6962
6963 editor.update(cx, |view, cx| {
6964 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6965 });
6966
6967 assert_eq!(
6968 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6969 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6970 );
6971
6972 editor.update(cx, |view, cx| {
6973 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6974 });
6975
6976 assert_eq!(
6977 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6978 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6979 );
6980
6981 editor.update(cx, |view, cx| {
6982 view.end_selection(cx);
6983 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6984 });
6985
6986 assert_eq!(
6987 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6988 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6989 );
6990
6991 editor.update(cx, |view, cx| {
6992 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6993 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6994 });
6995
6996 assert_eq!(
6997 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6998 [
6999 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
7000 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
7001 ]
7002 );
7003
7004 editor.update(cx, |view, cx| {
7005 view.end_selection(cx);
7006 });
7007
7008 assert_eq!(
7009 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7010 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
7011 );
7012 }
7013
7014 #[gpui::test]
7015 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
7016 cx.set_global(Settings::test(cx));
7017 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7018 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7019
7020 view.update(cx, |view, cx| {
7021 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7022 assert_eq!(
7023 view.selections.display_ranges(cx),
7024 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7025 );
7026 });
7027
7028 view.update(cx, |view, cx| {
7029 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7030 assert_eq!(
7031 view.selections.display_ranges(cx),
7032 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7033 );
7034 });
7035
7036 view.update(cx, |view, cx| {
7037 view.cancel(&Cancel, cx);
7038 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7039 assert_eq!(
7040 view.selections.display_ranges(cx),
7041 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7042 );
7043 });
7044 }
7045
7046 #[gpui::test]
7047 fn test_clone(cx: &mut gpui::MutableAppContext) {
7048 let (text, selection_ranges) = parse_marked_text(
7049 indoc! {"
7050 one
7051 two
7052 threeˇ
7053 four
7054 fiveˇ
7055 "},
7056 true,
7057 )
7058 .unwrap();
7059 cx.set_global(Settings::test(cx));
7060 let buffer = MultiBuffer::build_simple(&text, cx);
7061
7062 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7063
7064 editor.update(cx, |editor, cx| {
7065 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
7066 editor.fold_ranges(
7067 [
7068 Point::new(1, 0)..Point::new(2, 0),
7069 Point::new(3, 0)..Point::new(4, 0),
7070 ],
7071 cx,
7072 );
7073 });
7074
7075 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
7076 cx.add_window(Default::default(), |cx| editor.clone(cx))
7077 });
7078
7079 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
7080 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
7081
7082 assert_eq!(
7083 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
7084 editor.update(cx, |e, cx| e.display_text(cx))
7085 );
7086 assert_eq!(
7087 cloned_snapshot
7088 .folds_in_range(0..text.len())
7089 .collect::<Vec<_>>(),
7090 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
7091 );
7092 assert_set_eq!(
7093 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7094 editor.read(cx).selections.ranges(cx)
7095 );
7096 assert_set_eq!(
7097 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7098 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7099 );
7100 }
7101
7102 #[gpui::test]
7103 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7104 cx.set_global(Settings::test(cx));
7105 use workspace::Item;
7106 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
7107 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7108
7109 cx.add_window(Default::default(), |cx| {
7110 let mut editor = build_editor(buffer.clone(), cx);
7111 let handle = cx.handle();
7112 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7113
7114 fn pop_history(
7115 editor: &mut Editor,
7116 cx: &mut MutableAppContext,
7117 ) -> Option<NavigationEntry> {
7118 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7119 }
7120
7121 // Move the cursor a small distance.
7122 // Nothing is added to the navigation history.
7123 editor.change_selections(None, cx, |s| {
7124 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7125 });
7126 editor.change_selections(None, cx, |s| {
7127 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7128 });
7129 assert!(pop_history(&mut editor, cx).is_none());
7130
7131 // Move the cursor a large distance.
7132 // The history can jump back to the previous position.
7133 editor.change_selections(None, cx, |s| {
7134 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7135 });
7136 let nav_entry = pop_history(&mut editor, cx).unwrap();
7137 editor.navigate(nav_entry.data.unwrap(), cx);
7138 assert_eq!(nav_entry.item.id(), cx.view_id());
7139 assert_eq!(
7140 editor.selections.display_ranges(cx),
7141 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7142 );
7143 assert!(pop_history(&mut editor, cx).is_none());
7144
7145 // Move the cursor a small distance via the mouse.
7146 // Nothing is added to the navigation history.
7147 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7148 editor.end_selection(cx);
7149 assert_eq!(
7150 editor.selections.display_ranges(cx),
7151 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7152 );
7153 assert!(pop_history(&mut editor, cx).is_none());
7154
7155 // Move the cursor a large distance via the mouse.
7156 // The history can jump back to the previous position.
7157 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7158 editor.end_selection(cx);
7159 assert_eq!(
7160 editor.selections.display_ranges(cx),
7161 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7162 );
7163 let nav_entry = pop_history(&mut editor, cx).unwrap();
7164 editor.navigate(nav_entry.data.unwrap(), cx);
7165 assert_eq!(nav_entry.item.id(), cx.view_id());
7166 assert_eq!(
7167 editor.selections.display_ranges(cx),
7168 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7169 );
7170 assert!(pop_history(&mut editor, cx).is_none());
7171
7172 // Set scroll position to check later
7173 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7174 let original_scroll_position = editor.scroll_position;
7175 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7176
7177 // Jump to the end of the document and adjust scroll
7178 editor.move_to_end(&MoveToEnd, cx);
7179 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7180 assert_ne!(editor.scroll_position, original_scroll_position);
7181 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7182
7183 let nav_entry = pop_history(&mut editor, cx).unwrap();
7184 editor.navigate(nav_entry.data.unwrap(), cx);
7185 assert_eq!(editor.scroll_position, original_scroll_position);
7186 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7187
7188 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7189 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7190 invalid_anchor.text_anchor.buffer_id = Some(999);
7191 let invalid_point = Point::new(9999, 0);
7192 editor.navigate(
7193 Box::new(NavigationData {
7194 cursor_anchor: invalid_anchor.clone(),
7195 cursor_position: invalid_point,
7196 scroll_top_anchor: invalid_anchor.clone(),
7197 scroll_top_row: invalid_point.row,
7198 scroll_position: Default::default(),
7199 }),
7200 cx,
7201 );
7202 assert_eq!(
7203 editor.selections.display_ranges(cx),
7204 &[editor.max_point(cx)..editor.max_point(cx)]
7205 );
7206 assert_eq!(
7207 editor.scroll_position(cx),
7208 vec2f(0., editor.max_point(cx).row() as f32)
7209 );
7210
7211 editor
7212 });
7213 }
7214
7215 #[gpui::test]
7216 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7217 cx.set_global(Settings::test(cx));
7218 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7219 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7220
7221 view.update(cx, |view, cx| {
7222 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7223 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7224 view.end_selection(cx);
7225
7226 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7227 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7228 view.end_selection(cx);
7229 assert_eq!(
7230 view.selections.display_ranges(cx),
7231 [
7232 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7233 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7234 ]
7235 );
7236 });
7237
7238 view.update(cx, |view, cx| {
7239 view.cancel(&Cancel, cx);
7240 assert_eq!(
7241 view.selections.display_ranges(cx),
7242 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
7243 );
7244 });
7245
7246 view.update(cx, |view, cx| {
7247 view.cancel(&Cancel, cx);
7248 assert_eq!(
7249 view.selections.display_ranges(cx),
7250 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7251 );
7252 });
7253 }
7254
7255 #[gpui::test]
7256 fn test_fold(cx: &mut gpui::MutableAppContext) {
7257 cx.set_global(Settings::test(cx));
7258 let buffer = MultiBuffer::build_simple(
7259 &"
7260 impl Foo {
7261 // Hello!
7262
7263 fn a() {
7264 1
7265 }
7266
7267 fn b() {
7268 2
7269 }
7270
7271 fn c() {
7272 3
7273 }
7274 }
7275 "
7276 .unindent(),
7277 cx,
7278 );
7279 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7280
7281 view.update(cx, |view, cx| {
7282 view.change_selections(None, cx, |s| {
7283 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7284 });
7285 view.fold(&Fold, cx);
7286 assert_eq!(
7287 view.display_text(cx),
7288 "
7289 impl Foo {
7290 // Hello!
7291
7292 fn a() {
7293 1
7294 }
7295
7296 fn b() {…
7297 }
7298
7299 fn c() {…
7300 }
7301 }
7302 "
7303 .unindent(),
7304 );
7305
7306 view.fold(&Fold, cx);
7307 assert_eq!(
7308 view.display_text(cx),
7309 "
7310 impl Foo {…
7311 }
7312 "
7313 .unindent(),
7314 );
7315
7316 view.unfold_lines(&UnfoldLines, cx);
7317 assert_eq!(
7318 view.display_text(cx),
7319 "
7320 impl Foo {
7321 // Hello!
7322
7323 fn a() {
7324 1
7325 }
7326
7327 fn b() {…
7328 }
7329
7330 fn c() {…
7331 }
7332 }
7333 "
7334 .unindent(),
7335 );
7336
7337 view.unfold_lines(&UnfoldLines, cx);
7338 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7339 });
7340 }
7341
7342 #[gpui::test]
7343 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7344 cx.set_global(Settings::test(cx));
7345 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7346 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7347
7348 buffer.update(cx, |buffer, cx| {
7349 buffer.edit(
7350 vec![
7351 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7352 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7353 ],
7354 None,
7355 cx,
7356 );
7357 });
7358
7359 view.update(cx, |view, cx| {
7360 assert_eq!(
7361 view.selections.display_ranges(cx),
7362 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7363 );
7364
7365 view.move_down(&MoveDown, cx);
7366 assert_eq!(
7367 view.selections.display_ranges(cx),
7368 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7369 );
7370
7371 view.move_right(&MoveRight, cx);
7372 assert_eq!(
7373 view.selections.display_ranges(cx),
7374 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7375 );
7376
7377 view.move_left(&MoveLeft, cx);
7378 assert_eq!(
7379 view.selections.display_ranges(cx),
7380 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7381 );
7382
7383 view.move_up(&MoveUp, cx);
7384 assert_eq!(
7385 view.selections.display_ranges(cx),
7386 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7387 );
7388
7389 view.move_to_end(&MoveToEnd, cx);
7390 assert_eq!(
7391 view.selections.display_ranges(cx),
7392 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7393 );
7394
7395 view.move_to_beginning(&MoveToBeginning, cx);
7396 assert_eq!(
7397 view.selections.display_ranges(cx),
7398 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7399 );
7400
7401 view.change_selections(None, cx, |s| {
7402 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7403 });
7404 view.select_to_beginning(&SelectToBeginning, cx);
7405 assert_eq!(
7406 view.selections.display_ranges(cx),
7407 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7408 );
7409
7410 view.select_to_end(&SelectToEnd, cx);
7411 assert_eq!(
7412 view.selections.display_ranges(cx),
7413 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7414 );
7415 });
7416 }
7417
7418 #[gpui::test]
7419 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7420 cx.set_global(Settings::test(cx));
7421 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7422 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7423
7424 assert_eq!('ⓐ'.len_utf8(), 3);
7425 assert_eq!('α'.len_utf8(), 2);
7426
7427 view.update(cx, |view, cx| {
7428 view.fold_ranges(
7429 vec![
7430 Point::new(0, 6)..Point::new(0, 12),
7431 Point::new(1, 2)..Point::new(1, 4),
7432 Point::new(2, 4)..Point::new(2, 8),
7433 ],
7434 cx,
7435 );
7436 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
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 view.move_right(&MoveRight, cx);
7449 assert_eq!(
7450 view.selections.display_ranges(cx),
7451 &[empty_range(0, "ⓐⓑ…".len())]
7452 );
7453
7454 view.move_down(&MoveDown, 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, "ab".len())]
7463 );
7464 view.move_left(&MoveLeft, cx);
7465 assert_eq!(
7466 view.selections.display_ranges(cx),
7467 &[empty_range(1, "a".len())]
7468 );
7469
7470 view.move_down(&MoveDown, 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 view.move_right(&MoveRight, cx);
7486 assert_eq!(
7487 view.selections.display_ranges(cx),
7488 &[empty_range(2, "αβ…ε".len())]
7489 );
7490
7491 view.move_up(&MoveUp, cx);
7492 assert_eq!(
7493 view.selections.display_ranges(cx),
7494 &[empty_range(1, "ab…e".len())]
7495 );
7496 view.move_up(&MoveUp, 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 view.move_left(&MoveLeft, cx);
7512 assert_eq!(
7513 view.selections.display_ranges(cx),
7514 &[empty_range(0, "ⓐ".len())]
7515 );
7516 });
7517 }
7518
7519 #[gpui::test]
7520 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7521 cx.set_global(Settings::test(cx));
7522 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7523 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7524 view.update(cx, |view, cx| {
7525 view.change_selections(None, cx, |s| {
7526 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7527 });
7528 view.move_down(&MoveDown, cx);
7529 assert_eq!(
7530 view.selections.display_ranges(cx),
7531 &[empty_range(1, "abcd".len())]
7532 );
7533
7534 view.move_down(&MoveDown, cx);
7535 assert_eq!(
7536 view.selections.display_ranges(cx),
7537 &[empty_range(2, "αβγ".len())]
7538 );
7539
7540 view.move_down(&MoveDown, cx);
7541 assert_eq!(
7542 view.selections.display_ranges(cx),
7543 &[empty_range(3, "abcd".len())]
7544 );
7545
7546 view.move_down(&MoveDown, cx);
7547 assert_eq!(
7548 view.selections.display_ranges(cx),
7549 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7550 );
7551
7552 view.move_up(&MoveUp, cx);
7553 assert_eq!(
7554 view.selections.display_ranges(cx),
7555 &[empty_range(3, "abcd".len())]
7556 );
7557
7558 view.move_up(&MoveUp, cx);
7559 assert_eq!(
7560 view.selections.display_ranges(cx),
7561 &[empty_range(2, "αβγ".len())]
7562 );
7563 });
7564 }
7565
7566 #[gpui::test]
7567 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7568 cx.set_global(Settings::test(cx));
7569 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7570 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7571 view.update(cx, |view, cx| {
7572 view.change_selections(None, cx, |s| {
7573 s.select_display_ranges([
7574 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7575 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7576 ]);
7577 });
7578 });
7579
7580 view.update(cx, |view, cx| {
7581 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7582 assert_eq!(
7583 view.selections.display_ranges(cx),
7584 &[
7585 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7586 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7587 ]
7588 );
7589 });
7590
7591 view.update(cx, |view, cx| {
7592 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7593 assert_eq!(
7594 view.selections.display_ranges(cx),
7595 &[
7596 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7597 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7598 ]
7599 );
7600 });
7601
7602 view.update(cx, |view, cx| {
7603 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7604 assert_eq!(
7605 view.selections.display_ranges(cx),
7606 &[
7607 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7608 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7609 ]
7610 );
7611 });
7612
7613 view.update(cx, |view, cx| {
7614 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7615 assert_eq!(
7616 view.selections.display_ranges(cx),
7617 &[
7618 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7619 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7620 ]
7621 );
7622 });
7623
7624 // Moving to the end of line again is a no-op.
7625 view.update(cx, |view, cx| {
7626 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7627 assert_eq!(
7628 view.selections.display_ranges(cx),
7629 &[
7630 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7631 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7632 ]
7633 );
7634 });
7635
7636 view.update(cx, |view, cx| {
7637 view.move_left(&MoveLeft, cx);
7638 view.select_to_beginning_of_line(
7639 &SelectToBeginningOfLine {
7640 stop_at_soft_wraps: true,
7641 },
7642 cx,
7643 );
7644 assert_eq!(
7645 view.selections.display_ranges(cx),
7646 &[
7647 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7648 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7649 ]
7650 );
7651 });
7652
7653 view.update(cx, |view, cx| {
7654 view.select_to_beginning_of_line(
7655 &SelectToBeginningOfLine {
7656 stop_at_soft_wraps: true,
7657 },
7658 cx,
7659 );
7660 assert_eq!(
7661 view.selections.display_ranges(cx),
7662 &[
7663 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7664 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7665 ]
7666 );
7667 });
7668
7669 view.update(cx, |view, cx| {
7670 view.select_to_beginning_of_line(
7671 &SelectToBeginningOfLine {
7672 stop_at_soft_wraps: true,
7673 },
7674 cx,
7675 );
7676 assert_eq!(
7677 view.selections.display_ranges(cx),
7678 &[
7679 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7680 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7681 ]
7682 );
7683 });
7684
7685 view.update(cx, |view, cx| {
7686 view.select_to_end_of_line(
7687 &SelectToEndOfLine {
7688 stop_at_soft_wraps: true,
7689 },
7690 cx,
7691 );
7692 assert_eq!(
7693 view.selections.display_ranges(cx),
7694 &[
7695 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7696 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7697 ]
7698 );
7699 });
7700
7701 view.update(cx, |view, cx| {
7702 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7703 assert_eq!(view.display_text(cx), "ab\n de");
7704 assert_eq!(
7705 view.selections.display_ranges(cx),
7706 &[
7707 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7708 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7709 ]
7710 );
7711 });
7712
7713 view.update(cx, |view, cx| {
7714 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7715 assert_eq!(view.display_text(cx), "\n");
7716 assert_eq!(
7717 view.selections.display_ranges(cx),
7718 &[
7719 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7720 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7721 ]
7722 );
7723 });
7724 }
7725
7726 #[gpui::test]
7727 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7728 cx.set_global(Settings::test(cx));
7729 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7730 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7731 view.update(cx, |view, cx| {
7732 view.change_selections(None, cx, |s| {
7733 s.select_display_ranges([
7734 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7735 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7736 ])
7737 });
7738
7739 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7740 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7741
7742 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7743 assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx);
7744
7745 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7746 assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx);
7747
7748 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7749 assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7750
7751 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7752 assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx);
7753
7754 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7755 assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx);
7756
7757 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7758 assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7759
7760 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7761 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7762
7763 view.move_right(&MoveRight, cx);
7764 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7765 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7766
7767 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7768 assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx);
7769
7770 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7771 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7772 });
7773 }
7774
7775 #[gpui::test]
7776 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7777 cx.set_global(Settings::test(cx));
7778 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7779 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7780
7781 view.update(cx, |view, cx| {
7782 view.set_wrap_width(Some(140.), cx);
7783 assert_eq!(
7784 view.display_text(cx),
7785 "use one::{\n two::three::\n four::five\n};"
7786 );
7787
7788 view.change_selections(None, cx, |s| {
7789 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7790 });
7791
7792 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7793 assert_eq!(
7794 view.selections.display_ranges(cx),
7795 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7796 );
7797
7798 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7799 assert_eq!(
7800 view.selections.display_ranges(cx),
7801 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7802 );
7803
7804 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7805 assert_eq!(
7806 view.selections.display_ranges(cx),
7807 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7808 );
7809
7810 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7811 assert_eq!(
7812 view.selections.display_ranges(cx),
7813 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7814 );
7815
7816 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7817 assert_eq!(
7818 view.selections.display_ranges(cx),
7819 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7820 );
7821
7822 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7823 assert_eq!(
7824 view.selections.display_ranges(cx),
7825 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7826 );
7827 });
7828 }
7829
7830 #[gpui::test]
7831 async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
7832 let mut cx = EditorTestContext::new(cx).await;
7833 cx.set_state("one «two threeˇ» four");
7834 cx.update_editor(|editor, cx| {
7835 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7836 assert_eq!(editor.text(cx), " four");
7837 });
7838 }
7839
7840 #[gpui::test]
7841 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7842 cx.set_global(Settings::test(cx));
7843 let buffer = MultiBuffer::build_simple("one two three four", cx);
7844 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7845
7846 view.update(cx, |view, cx| {
7847 view.change_selections(None, cx, |s| {
7848 s.select_display_ranges([
7849 // an empty selection - the preceding word fragment is deleted
7850 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7851 // characters selected - they are deleted
7852 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7853 ])
7854 });
7855 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7856 });
7857
7858 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7859
7860 view.update(cx, |view, cx| {
7861 view.change_selections(None, cx, |s| {
7862 s.select_display_ranges([
7863 // an empty selection - the following word fragment is deleted
7864 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7865 // characters selected - they are deleted
7866 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7867 ])
7868 });
7869 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7870 });
7871
7872 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7873 }
7874
7875 #[gpui::test]
7876 fn test_newline(cx: &mut gpui::MutableAppContext) {
7877 cx.set_global(Settings::test(cx));
7878 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7879 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7880
7881 view.update(cx, |view, cx| {
7882 view.change_selections(None, cx, |s| {
7883 s.select_display_ranges([
7884 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7885 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7886 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7887 ])
7888 });
7889
7890 view.newline(&Newline, cx);
7891 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7892 });
7893 }
7894
7895 #[gpui::test]
7896 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7897 cx.set_global(Settings::test(cx));
7898 let buffer = MultiBuffer::build_simple(
7899 "
7900 a
7901 b(
7902 X
7903 )
7904 c(
7905 X
7906 )
7907 "
7908 .unindent()
7909 .as_str(),
7910 cx,
7911 );
7912
7913 let (_, editor) = cx.add_window(Default::default(), |cx| {
7914 let mut editor = build_editor(buffer.clone(), cx);
7915 editor.change_selections(None, cx, |s| {
7916 s.select_ranges([
7917 Point::new(2, 4)..Point::new(2, 5),
7918 Point::new(5, 4)..Point::new(5, 5),
7919 ])
7920 });
7921 editor
7922 });
7923
7924 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7925 buffer.update(cx, |buffer, cx| {
7926 buffer.edit(
7927 [
7928 (Point::new(1, 2)..Point::new(3, 0), ""),
7929 (Point::new(4, 2)..Point::new(6, 0), ""),
7930 ],
7931 None,
7932 cx,
7933 );
7934 assert_eq!(
7935 buffer.read(cx).text(),
7936 "
7937 a
7938 b()
7939 c()
7940 "
7941 .unindent()
7942 );
7943 });
7944
7945 editor.update(cx, |editor, cx| {
7946 assert_eq!(
7947 editor.selections.ranges(cx),
7948 &[
7949 Point::new(1, 2)..Point::new(1, 2),
7950 Point::new(2, 2)..Point::new(2, 2),
7951 ],
7952 );
7953
7954 editor.newline(&Newline, cx);
7955 assert_eq!(
7956 editor.text(cx),
7957 "
7958 a
7959 b(
7960 )
7961 c(
7962 )
7963 "
7964 .unindent()
7965 );
7966
7967 // The selections are moved after the inserted newlines
7968 assert_eq!(
7969 editor.selections.ranges(cx),
7970 &[
7971 Point::new(2, 0)..Point::new(2, 0),
7972 Point::new(4, 0)..Point::new(4, 0),
7973 ],
7974 );
7975 });
7976 }
7977
7978 #[gpui::test]
7979 async fn test_newline_below(cx: &mut gpui::TestAppContext) {
7980 let mut cx = EditorTestContext::new(cx).await;
7981 cx.update(|cx| {
7982 cx.update_global::<Settings, _, _>(|settings, _| {
7983 settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
7984 });
7985 });
7986
7987 let language = Arc::new(
7988 Language::new(
7989 LanguageConfig::default(),
7990 Some(tree_sitter_rust::language()),
7991 )
7992 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
7993 .unwrap(),
7994 );
7995 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
7996
7997 cx.set_state(indoc! {"
7998 const a: ˇA = (
7999 (ˇ
8000 «const_functionˇ»(ˇ),
8001 so«mˇ»et«hˇ»ing_ˇelse,ˇ
8002 )ˇ
8003 ˇ);ˇ
8004 "});
8005 cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
8006 cx.assert_editor_state(indoc! {"
8007 const a: A = (
8008 ˇ
8009 (
8010 ˇ
8011 const_function(),
8012 ˇ
8013 ˇ
8014 something_else,
8015 ˇ
8016 ˇ
8017 ˇ
8018 ˇ
8019 )
8020 ˇ
8021 );
8022 ˇ
8023 ˇ
8024 "});
8025 }
8026
8027 #[gpui::test]
8028 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
8029 cx.set_global(Settings::test(cx));
8030 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
8031 let (_, editor) = cx.add_window(Default::default(), |cx| {
8032 let mut editor = build_editor(buffer.clone(), cx);
8033 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
8034 editor
8035 });
8036
8037 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8038 buffer.update(cx, |buffer, cx| {
8039 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
8040 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
8041 });
8042
8043 editor.update(cx, |editor, cx| {
8044 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
8045
8046 editor.insert("Z", cx);
8047 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
8048
8049 // The selections are moved after the inserted characters
8050 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
8051 });
8052 }
8053
8054 #[gpui::test]
8055 async fn test_tab(cx: &mut gpui::TestAppContext) {
8056 let mut cx = EditorTestContext::new(cx).await;
8057 cx.update(|cx| {
8058 cx.update_global::<Settings, _, _>(|settings, _| {
8059 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
8060 });
8061 });
8062 cx.set_state(indoc! {"
8063 ˇabˇc
8064 ˇ🏀ˇ🏀ˇefg
8065 dˇ
8066 "});
8067 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8068 cx.assert_editor_state(indoc! {"
8069 ˇab ˇc
8070 ˇ🏀 ˇ🏀 ˇefg
8071 d ˇ
8072 "});
8073
8074 cx.set_state(indoc! {"
8075 a
8076 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8077 "});
8078 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8079 cx.assert_editor_state(indoc! {"
8080 a
8081 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8082 "});
8083 }
8084
8085 #[gpui::test]
8086 async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
8087 let mut cx = EditorTestContext::new(cx).await;
8088 let language = Arc::new(
8089 Language::new(
8090 LanguageConfig::default(),
8091 Some(tree_sitter_rust::language()),
8092 )
8093 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8094 .unwrap(),
8095 );
8096 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8097
8098 // cursors that are already at the suggested indent level insert
8099 // a soft tab. cursors that are to the left of the suggested indent
8100 // auto-indent their line.
8101 cx.set_state(indoc! {"
8102 ˇ
8103 const a: B = (
8104 c(
8105 d(
8106 ˇ
8107 )
8108 ˇ
8109 ˇ )
8110 );
8111 "});
8112 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8113 cx.assert_editor_state(indoc! {"
8114 ˇ
8115 const a: B = (
8116 c(
8117 d(
8118 ˇ
8119 )
8120 ˇ
8121 ˇ)
8122 );
8123 "});
8124
8125 // handle auto-indent when there are multiple cursors on the same line
8126 cx.set_state(indoc! {"
8127 const a: B = (
8128 c(
8129 ˇ ˇ
8130 ˇ )
8131 );
8132 "});
8133 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8134 cx.assert_editor_state(indoc! {"
8135 const a: B = (
8136 c(
8137 ˇ
8138 ˇ)
8139 );
8140 "});
8141 }
8142
8143 #[gpui::test]
8144 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
8145 let mut cx = EditorTestContext::new(cx).await;
8146
8147 cx.set_state(indoc! {"
8148 «oneˇ» «twoˇ»
8149 three
8150 four
8151 "});
8152 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8153 cx.assert_editor_state(indoc! {"
8154 «oneˇ» «twoˇ»
8155 three
8156 four
8157 "});
8158
8159 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8160 cx.assert_editor_state(indoc! {"
8161 «oneˇ» «twoˇ»
8162 three
8163 four
8164 "});
8165
8166 // select across line ending
8167 cx.set_state(indoc! {"
8168 one two
8169 t«hree
8170 ˇ» four
8171 "});
8172 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8173 cx.assert_editor_state(indoc! {"
8174 one two
8175 t«hree
8176 ˇ» four
8177 "});
8178
8179 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8180 cx.assert_editor_state(indoc! {"
8181 one two
8182 t«hree
8183 ˇ» four
8184 "});
8185
8186 // Ensure that indenting/outdenting works when the cursor is at column 0.
8187 cx.set_state(indoc! {"
8188 one two
8189 ˇthree
8190 four
8191 "});
8192 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8193 cx.assert_editor_state(indoc! {"
8194 one two
8195 ˇthree
8196 four
8197 "});
8198
8199 cx.set_state(indoc! {"
8200 one two
8201 ˇ three
8202 four
8203 "});
8204 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8205 cx.assert_editor_state(indoc! {"
8206 one two
8207 ˇthree
8208 four
8209 "});
8210 }
8211
8212 #[gpui::test]
8213 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8214 let mut cx = EditorTestContext::new(cx).await;
8215 cx.update(|cx| {
8216 cx.update_global::<Settings, _, _>(|settings, _| {
8217 settings.editor_overrides.hard_tabs = Some(true);
8218 });
8219 });
8220
8221 // select two ranges on one line
8222 cx.set_state(indoc! {"
8223 «oneˇ» «twoˇ»
8224 three
8225 four
8226 "});
8227 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8228 cx.assert_editor_state(indoc! {"
8229 \t«oneˇ» «twoˇ»
8230 three
8231 four
8232 "});
8233 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8234 cx.assert_editor_state(indoc! {"
8235 \t\t«oneˇ» «twoˇ»
8236 three
8237 four
8238 "});
8239 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8240 cx.assert_editor_state(indoc! {"
8241 \t«oneˇ» «twoˇ»
8242 three
8243 four
8244 "});
8245 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8246 cx.assert_editor_state(indoc! {"
8247 «oneˇ» «twoˇ»
8248 three
8249 four
8250 "});
8251
8252 // select across a line ending
8253 cx.set_state(indoc! {"
8254 one two
8255 t«hree
8256 ˇ»four
8257 "});
8258 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8259 cx.assert_editor_state(indoc! {"
8260 one two
8261 \tt«hree
8262 ˇ»four
8263 "});
8264 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8265 cx.assert_editor_state(indoc! {"
8266 one two
8267 \t\tt«hree
8268 ˇ»four
8269 "});
8270 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8271 cx.assert_editor_state(indoc! {"
8272 one two
8273 \tt«hree
8274 ˇ»four
8275 "});
8276 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8277 cx.assert_editor_state(indoc! {"
8278 one two
8279 t«hree
8280 ˇ»four
8281 "});
8282
8283 // Ensure that indenting/outdenting works when the cursor is at column 0.
8284 cx.set_state(indoc! {"
8285 one two
8286 ˇthree
8287 four
8288 "});
8289 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8290 cx.assert_editor_state(indoc! {"
8291 one two
8292 ˇthree
8293 four
8294 "});
8295 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8296 cx.assert_editor_state(indoc! {"
8297 one two
8298 \tˇthree
8299 four
8300 "});
8301 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8302 cx.assert_editor_state(indoc! {"
8303 one two
8304 ˇthree
8305 four
8306 "});
8307 }
8308
8309 #[gpui::test]
8310 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8311 cx.set_global(
8312 Settings::test(cx)
8313 .with_language_defaults(
8314 "TOML",
8315 EditorSettings {
8316 tab_size: Some(2.try_into().unwrap()),
8317 ..Default::default()
8318 },
8319 )
8320 .with_language_defaults(
8321 "Rust",
8322 EditorSettings {
8323 tab_size: Some(4.try_into().unwrap()),
8324 ..Default::default()
8325 },
8326 ),
8327 );
8328 let toml_language = Arc::new(Language::new(
8329 LanguageConfig {
8330 name: "TOML".into(),
8331 ..Default::default()
8332 },
8333 None,
8334 ));
8335 let rust_language = Arc::new(Language::new(
8336 LanguageConfig {
8337 name: "Rust".into(),
8338 ..Default::default()
8339 },
8340 None,
8341 ));
8342
8343 let toml_buffer = cx
8344 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8345 let rust_buffer = cx.add_model(|cx| {
8346 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8347 });
8348 let multibuffer = cx.add_model(|cx| {
8349 let mut multibuffer = MultiBuffer::new(0);
8350 multibuffer.push_excerpts(
8351 toml_buffer.clone(),
8352 [ExcerptRange {
8353 context: Point::new(0, 0)..Point::new(2, 0),
8354 primary: None,
8355 }],
8356 cx,
8357 );
8358 multibuffer.push_excerpts(
8359 rust_buffer.clone(),
8360 [ExcerptRange {
8361 context: Point::new(0, 0)..Point::new(1, 0),
8362 primary: None,
8363 }],
8364 cx,
8365 );
8366 multibuffer
8367 });
8368
8369 cx.add_window(Default::default(), |cx| {
8370 let mut editor = build_editor(multibuffer, cx);
8371
8372 assert_eq!(
8373 editor.text(cx),
8374 indoc! {"
8375 a = 1
8376 b = 2
8377
8378 const c: usize = 3;
8379 "}
8380 );
8381
8382 select_ranges(
8383 &mut editor,
8384 indoc! {"
8385 «aˇ» = 1
8386 b = 2
8387
8388 «const c:ˇ» usize = 3;
8389 "},
8390 cx,
8391 );
8392
8393 editor.tab(&Tab, cx);
8394 assert_text_with_selections(
8395 &mut editor,
8396 indoc! {"
8397 «aˇ» = 1
8398 b = 2
8399
8400 «const c:ˇ» usize = 3;
8401 "},
8402 cx,
8403 );
8404 editor.tab_prev(&TabPrev, cx);
8405 assert_text_with_selections(
8406 &mut editor,
8407 indoc! {"
8408 «aˇ» = 1
8409 b = 2
8410
8411 «const c:ˇ» usize = 3;
8412 "},
8413 cx,
8414 );
8415
8416 editor
8417 });
8418 }
8419
8420 #[gpui::test]
8421 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8422 let mut cx = EditorTestContext::new(cx).await;
8423
8424 // Basic backspace
8425 cx.set_state(indoc! {"
8426 onˇe two three
8427 fou«rˇ» five six
8428 seven «ˇeight nine
8429 »ten
8430 "});
8431 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8432 cx.assert_editor_state(indoc! {"
8433 oˇe two three
8434 fouˇ five six
8435 seven ˇten
8436 "});
8437
8438 // Test backspace inside and around indents
8439 cx.set_state(indoc! {"
8440 zero
8441 ˇone
8442 ˇtwo
8443 ˇ ˇ ˇ three
8444 ˇ ˇ four
8445 "});
8446 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8447 cx.assert_editor_state(indoc! {"
8448 zero
8449 ˇone
8450 ˇtwo
8451 ˇ threeˇ four
8452 "});
8453
8454 // Test backspace with line_mode set to true
8455 cx.update_editor(|e, _| e.selections.line_mode = true);
8456 cx.set_state(indoc! {"
8457 The ˇquick ˇbrown
8458 fox jumps over
8459 the lazy dog
8460 ˇThe qu«ick bˇ»rown"});
8461 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8462 cx.assert_editor_state(indoc! {"
8463 ˇfox jumps over
8464 the lazy dogˇ"});
8465 }
8466
8467 #[gpui::test]
8468 async fn test_delete(cx: &mut gpui::TestAppContext) {
8469 let mut cx = EditorTestContext::new(cx).await;
8470
8471 cx.set_state(indoc! {"
8472 onˇe two three
8473 fou«rˇ» five six
8474 seven «ˇeight nine
8475 »ten
8476 "});
8477 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8478 cx.assert_editor_state(indoc! {"
8479 onˇ two three
8480 fouˇ five six
8481 seven ˇten
8482 "});
8483
8484 // Test backspace with line_mode set to true
8485 cx.update_editor(|e, _| e.selections.line_mode = true);
8486 cx.set_state(indoc! {"
8487 The ˇquick ˇbrown
8488 fox «ˇjum»ps over
8489 the lazy dog
8490 ˇThe qu«ick bˇ»rown"});
8491 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8492 cx.assert_editor_state("ˇthe lazy dogˇ");
8493 }
8494
8495 #[gpui::test]
8496 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8497 cx.set_global(Settings::test(cx));
8498 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8499 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8500 view.update(cx, |view, cx| {
8501 view.change_selections(None, cx, |s| {
8502 s.select_display_ranges([
8503 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8504 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8505 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8506 ])
8507 });
8508 view.delete_line(&DeleteLine, cx);
8509 assert_eq!(view.display_text(cx), "ghi");
8510 assert_eq!(
8511 view.selections.display_ranges(cx),
8512 vec![
8513 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8514 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8515 ]
8516 );
8517 });
8518
8519 cx.set_global(Settings::test(cx));
8520 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8521 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8522 view.update(cx, |view, cx| {
8523 view.change_selections(None, cx, |s| {
8524 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8525 });
8526 view.delete_line(&DeleteLine, cx);
8527 assert_eq!(view.display_text(cx), "ghi\n");
8528 assert_eq!(
8529 view.selections.display_ranges(cx),
8530 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8531 );
8532 });
8533 }
8534
8535 #[gpui::test]
8536 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8537 cx.set_global(Settings::test(cx));
8538 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8539 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8540 view.update(cx, |view, cx| {
8541 view.change_selections(None, cx, |s| {
8542 s.select_display_ranges([
8543 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8544 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8545 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8546 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8547 ])
8548 });
8549 view.duplicate_line(&DuplicateLine, cx);
8550 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8551 assert_eq!(
8552 view.selections.display_ranges(cx),
8553 vec![
8554 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8555 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8556 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8557 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8558 ]
8559 );
8560 });
8561
8562 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8563 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8564 view.update(cx, |view, cx| {
8565 view.change_selections(None, cx, |s| {
8566 s.select_display_ranges([
8567 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8568 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8569 ])
8570 });
8571 view.duplicate_line(&DuplicateLine, cx);
8572 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8573 assert_eq!(
8574 view.selections.display_ranges(cx),
8575 vec![
8576 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8577 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8578 ]
8579 );
8580 });
8581 }
8582
8583 #[gpui::test]
8584 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8585 cx.set_global(Settings::test(cx));
8586 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8587 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8588 view.update(cx, |view, cx| {
8589 view.fold_ranges(
8590 vec![
8591 Point::new(0, 2)..Point::new(1, 2),
8592 Point::new(2, 3)..Point::new(4, 1),
8593 Point::new(7, 0)..Point::new(8, 4),
8594 ],
8595 cx,
8596 );
8597 view.change_selections(None, cx, |s| {
8598 s.select_display_ranges([
8599 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8600 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8601 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8602 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8603 ])
8604 });
8605 assert_eq!(
8606 view.display_text(cx),
8607 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8608 );
8609
8610 view.move_line_up(&MoveLineUp, cx);
8611 assert_eq!(
8612 view.display_text(cx),
8613 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8614 );
8615 assert_eq!(
8616 view.selections.display_ranges(cx),
8617 vec![
8618 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8619 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8620 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8621 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8622 ]
8623 );
8624 });
8625
8626 view.update(cx, |view, cx| {
8627 view.move_line_down(&MoveLineDown, cx);
8628 assert_eq!(
8629 view.display_text(cx),
8630 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8631 );
8632 assert_eq!(
8633 view.selections.display_ranges(cx),
8634 vec![
8635 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8636 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8637 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8638 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8639 ]
8640 );
8641 });
8642
8643 view.update(cx, |view, cx| {
8644 view.move_line_down(&MoveLineDown, cx);
8645 assert_eq!(
8646 view.display_text(cx),
8647 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8648 );
8649 assert_eq!(
8650 view.selections.display_ranges(cx),
8651 vec![
8652 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8653 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8654 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8655 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8656 ]
8657 );
8658 });
8659
8660 view.update(cx, |view, cx| {
8661 view.move_line_up(&MoveLineUp, cx);
8662 assert_eq!(
8663 view.display_text(cx),
8664 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8665 );
8666 assert_eq!(
8667 view.selections.display_ranges(cx),
8668 vec![
8669 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8670 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8671 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8672 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8673 ]
8674 );
8675 });
8676 }
8677
8678 #[gpui::test]
8679 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8680 cx.set_global(Settings::test(cx));
8681 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8682 let snapshot = buffer.read(cx).snapshot(cx);
8683 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8684 editor.update(cx, |editor, cx| {
8685 editor.insert_blocks(
8686 [BlockProperties {
8687 style: BlockStyle::Fixed,
8688 position: snapshot.anchor_after(Point::new(2, 0)),
8689 disposition: BlockDisposition::Below,
8690 height: 1,
8691 render: Arc::new(|_| Empty::new().boxed()),
8692 }],
8693 cx,
8694 );
8695 editor.change_selections(None, cx, |s| {
8696 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8697 });
8698 editor.move_line_down(&MoveLineDown, cx);
8699 });
8700 }
8701
8702 #[gpui::test]
8703 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8704 cx.set_global(Settings::test(cx));
8705
8706 cx.add_window(Default::default(), |cx| {
8707 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8708
8709 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8710 editor.transpose(&Default::default(), cx);
8711 assert_eq!(editor.text(cx), "bac");
8712 assert_eq!(editor.selections.ranges(cx), [2..2]);
8713
8714 editor.transpose(&Default::default(), cx);
8715 assert_eq!(editor.text(cx), "bca");
8716 assert_eq!(editor.selections.ranges(cx), [3..3]);
8717
8718 editor.transpose(&Default::default(), cx);
8719 assert_eq!(editor.text(cx), "bac");
8720 assert_eq!(editor.selections.ranges(cx), [3..3]);
8721
8722 editor
8723 })
8724 .1;
8725
8726 cx.add_window(Default::default(), |cx| {
8727 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8728
8729 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8730 editor.transpose(&Default::default(), cx);
8731 assert_eq!(editor.text(cx), "acb\nde");
8732 assert_eq!(editor.selections.ranges(cx), [3..3]);
8733
8734 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8735 editor.transpose(&Default::default(), cx);
8736 assert_eq!(editor.text(cx), "acbd\ne");
8737 assert_eq!(editor.selections.ranges(cx), [5..5]);
8738
8739 editor.transpose(&Default::default(), cx);
8740 assert_eq!(editor.text(cx), "acbde\n");
8741 assert_eq!(editor.selections.ranges(cx), [6..6]);
8742
8743 editor.transpose(&Default::default(), cx);
8744 assert_eq!(editor.text(cx), "acbd\ne");
8745 assert_eq!(editor.selections.ranges(cx), [6..6]);
8746
8747 editor
8748 })
8749 .1;
8750
8751 cx.add_window(Default::default(), |cx| {
8752 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8753
8754 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8755 editor.transpose(&Default::default(), cx);
8756 assert_eq!(editor.text(cx), "bacd\ne");
8757 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8758
8759 editor.transpose(&Default::default(), cx);
8760 assert_eq!(editor.text(cx), "bcade\n");
8761 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8762
8763 editor.transpose(&Default::default(), cx);
8764 assert_eq!(editor.text(cx), "bcda\ne");
8765 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8766
8767 editor.transpose(&Default::default(), cx);
8768 assert_eq!(editor.text(cx), "bcade\n");
8769 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8770
8771 editor.transpose(&Default::default(), cx);
8772 assert_eq!(editor.text(cx), "bcaed\n");
8773 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8774
8775 editor
8776 })
8777 .1;
8778
8779 cx.add_window(Default::default(), |cx| {
8780 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8781
8782 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8783 editor.transpose(&Default::default(), cx);
8784 assert_eq!(editor.text(cx), "🏀🍐✋");
8785 assert_eq!(editor.selections.ranges(cx), [8..8]);
8786
8787 editor.transpose(&Default::default(), cx);
8788 assert_eq!(editor.text(cx), "🏀✋🍐");
8789 assert_eq!(editor.selections.ranges(cx), [11..11]);
8790
8791 editor.transpose(&Default::default(), cx);
8792 assert_eq!(editor.text(cx), "🏀🍐✋");
8793 assert_eq!(editor.selections.ranges(cx), [11..11]);
8794
8795 editor
8796 })
8797 .1;
8798 }
8799
8800 #[gpui::test]
8801 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8802 let mut cx = EditorTestContext::new(cx).await;
8803
8804 cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
8805 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8806 cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
8807
8808 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8809 cx.set_state("two ˇfour ˇsix ˇ");
8810 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8811 cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
8812
8813 // Paste again but with only two cursors. Since the number of cursors doesn't
8814 // match the number of slices in the clipboard, the entire clipboard text
8815 // is pasted at each cursor.
8816 cx.set_state("ˇtwo one✅ four three six five ˇ");
8817 cx.update_editor(|e, cx| {
8818 e.handle_input("( ", cx);
8819 e.paste(&Paste, cx);
8820 e.handle_input(") ", cx);
8821 });
8822 cx.assert_editor_state(indoc! {"
8823 ( one✅
8824 three
8825 five ) ˇtwo one✅ four three six five ( one✅
8826 three
8827 five ) ˇ"});
8828
8829 // Cut with three selections, one of which is full-line.
8830 cx.set_state(indoc! {"
8831 1«2ˇ»3
8832 4ˇ567
8833 «8ˇ»9"});
8834 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8835 cx.assert_editor_state(indoc! {"
8836 1ˇ3
8837 ˇ9"});
8838
8839 // Paste with three selections, noticing how the copied selection that was full-line
8840 // gets inserted before the second cursor.
8841 cx.set_state(indoc! {"
8842 1ˇ3
8843 9ˇ
8844 «oˇ»ne"});
8845 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8846 cx.assert_editor_state(indoc! {"
8847 12ˇ3
8848 4567
8849 9ˇ
8850 8ˇne"});
8851
8852 // Copy with a single cursor only, which writes the whole line into the clipboard.
8853 cx.set_state(indoc! {"
8854 The quick brown
8855 fox juˇmps over
8856 the lazy dog"});
8857 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8858 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8859
8860 // Paste with three selections, noticing how the copied full-line selection is inserted
8861 // before the empty selections but replaces the selection that is non-empty.
8862 cx.set_state(indoc! {"
8863 Tˇhe quick brown
8864 «foˇ»x jumps over
8865 tˇhe lazy dog"});
8866 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8867 cx.assert_editor_state(indoc! {"
8868 fox jumps over
8869 Tˇhe quick brown
8870 fox jumps over
8871 ˇx jumps over
8872 fox jumps over
8873 tˇhe lazy dog"});
8874 }
8875
8876 #[gpui::test]
8877 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
8878 let mut cx = EditorTestContext::new(cx).await;
8879 let language = Arc::new(Language::new(
8880 LanguageConfig::default(),
8881 Some(tree_sitter_rust::language()),
8882 ));
8883 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8884
8885 // Cut an indented block, without the leading whitespace.
8886 cx.set_state(indoc! {"
8887 const a: B = (
8888 c(),
8889 «d(
8890 e,
8891 f
8892 )ˇ»
8893 );
8894 "});
8895 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8896 cx.assert_editor_state(indoc! {"
8897 const a: B = (
8898 c(),
8899 ˇ
8900 );
8901 "});
8902
8903 // Paste it at the same position.
8904 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8905 cx.assert_editor_state(indoc! {"
8906 const a: B = (
8907 c(),
8908 d(
8909 e,
8910 f
8911 )ˇ
8912 );
8913 "});
8914
8915 // Paste it at a line with a lower indent level.
8916 cx.set_state(indoc! {"
8917 ˇ
8918 const a: B = (
8919 c(),
8920 );
8921 "});
8922 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8923 cx.assert_editor_state(indoc! {"
8924 d(
8925 e,
8926 f
8927 )ˇ
8928 const a: B = (
8929 c(),
8930 );
8931 "});
8932
8933 // Cut an indented block, with the leading whitespace.
8934 cx.set_state(indoc! {"
8935 const a: B = (
8936 c(),
8937 « d(
8938 e,
8939 f
8940 )
8941 ˇ»);
8942 "});
8943 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8944 cx.assert_editor_state(indoc! {"
8945 const a: B = (
8946 c(),
8947 ˇ);
8948 "});
8949
8950 // Paste it at the same position.
8951 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8952 cx.assert_editor_state(indoc! {"
8953 const a: B = (
8954 c(),
8955 d(
8956 e,
8957 f
8958 )
8959 ˇ);
8960 "});
8961
8962 // Paste it at a line with a higher indent level.
8963 cx.set_state(indoc! {"
8964 const a: B = (
8965 c(),
8966 d(
8967 e,
8968 fˇ
8969 )
8970 );
8971 "});
8972 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8973 cx.assert_editor_state(indoc! {"
8974 const a: B = (
8975 c(),
8976 d(
8977 e,
8978 f d(
8979 e,
8980 f
8981 )
8982 ˇ
8983 )
8984 );
8985 "});
8986 }
8987
8988 #[gpui::test]
8989 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8990 cx.set_global(Settings::test(cx));
8991 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8992 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8993 view.update(cx, |view, cx| {
8994 view.select_all(&SelectAll, cx);
8995 assert_eq!(
8996 view.selections.display_ranges(cx),
8997 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8998 );
8999 });
9000 }
9001
9002 #[gpui::test]
9003 fn test_select_line(cx: &mut gpui::MutableAppContext) {
9004 cx.set_global(Settings::test(cx));
9005 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
9006 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9007 view.update(cx, |view, cx| {
9008 view.change_selections(None, cx, |s| {
9009 s.select_display_ranges([
9010 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9011 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9012 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9013 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
9014 ])
9015 });
9016 view.select_line(&SelectLine, cx);
9017 assert_eq!(
9018 view.selections.display_ranges(cx),
9019 vec![
9020 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
9021 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
9022 ]
9023 );
9024 });
9025
9026 view.update(cx, |view, cx| {
9027 view.select_line(&SelectLine, cx);
9028 assert_eq!(
9029 view.selections.display_ranges(cx),
9030 vec![
9031 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
9032 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
9033 ]
9034 );
9035 });
9036
9037 view.update(cx, |view, cx| {
9038 view.select_line(&SelectLine, cx);
9039 assert_eq!(
9040 view.selections.display_ranges(cx),
9041 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
9042 );
9043 });
9044 }
9045
9046 #[gpui::test]
9047 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
9048 cx.set_global(Settings::test(cx));
9049 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
9050 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9051 view.update(cx, |view, cx| {
9052 view.fold_ranges(
9053 vec![
9054 Point::new(0, 2)..Point::new(1, 2),
9055 Point::new(2, 3)..Point::new(4, 1),
9056 Point::new(7, 0)..Point::new(8, 4),
9057 ],
9058 cx,
9059 );
9060 view.change_selections(None, cx, |s| {
9061 s.select_display_ranges([
9062 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9063 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9064 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9065 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9066 ])
9067 });
9068 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
9069 });
9070
9071 view.update(cx, |view, cx| {
9072 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9073 assert_eq!(
9074 view.display_text(cx),
9075 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9076 );
9077 assert_eq!(
9078 view.selections.display_ranges(cx),
9079 [
9080 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9081 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9082 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9083 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9084 ]
9085 );
9086 });
9087
9088 view.update(cx, |view, cx| {
9089 view.change_selections(None, cx, |s| {
9090 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9091 });
9092 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9093 assert_eq!(
9094 view.display_text(cx),
9095 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9096 );
9097 assert_eq!(
9098 view.selections.display_ranges(cx),
9099 [
9100 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9101 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9102 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9103 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9104 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9105 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9106 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9107 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9108 ]
9109 );
9110 });
9111 }
9112
9113 #[gpui::test]
9114 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9115 cx.set_global(Settings::test(cx));
9116 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9117 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9118
9119 view.update(cx, |view, cx| {
9120 view.change_selections(None, cx, |s| {
9121 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9122 });
9123 });
9124 view.update(cx, |view, cx| {
9125 view.add_selection_above(&AddSelectionAbove, cx);
9126 assert_eq!(
9127 view.selections.display_ranges(cx),
9128 vec![
9129 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9130 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9131 ]
9132 );
9133 });
9134
9135 view.update(cx, |view, cx| {
9136 view.add_selection_above(&AddSelectionAbove, cx);
9137 assert_eq!(
9138 view.selections.display_ranges(cx),
9139 vec![
9140 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9141 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9142 ]
9143 );
9144 });
9145
9146 view.update(cx, |view, cx| {
9147 view.add_selection_below(&AddSelectionBelow, cx);
9148 assert_eq!(
9149 view.selections.display_ranges(cx),
9150 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9151 );
9152
9153 view.undo_selection(&UndoSelection, cx);
9154 assert_eq!(
9155 view.selections.display_ranges(cx),
9156 vec![
9157 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9158 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9159 ]
9160 );
9161
9162 view.redo_selection(&RedoSelection, cx);
9163 assert_eq!(
9164 view.selections.display_ranges(cx),
9165 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
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![
9174 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9175 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9176 ]
9177 );
9178 });
9179
9180 view.update(cx, |view, cx| {
9181 view.add_selection_below(&AddSelectionBelow, cx);
9182 assert_eq!(
9183 view.selections.display_ranges(cx),
9184 vec![
9185 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9186 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9187 ]
9188 );
9189 });
9190
9191 view.update(cx, |view, cx| {
9192 view.change_selections(None, cx, |s| {
9193 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9194 });
9195 });
9196 view.update(cx, |view, cx| {
9197 view.add_selection_below(&AddSelectionBelow, cx);
9198 assert_eq!(
9199 view.selections.display_ranges(cx),
9200 vec![
9201 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9202 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9203 ]
9204 );
9205 });
9206
9207 view.update(cx, |view, cx| {
9208 view.add_selection_below(&AddSelectionBelow, cx);
9209 assert_eq!(
9210 view.selections.display_ranges(cx),
9211 vec![
9212 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9213 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9214 ]
9215 );
9216 });
9217
9218 view.update(cx, |view, cx| {
9219 view.add_selection_above(&AddSelectionAbove, cx);
9220 assert_eq!(
9221 view.selections.display_ranges(cx),
9222 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9223 );
9224 });
9225
9226 view.update(cx, |view, cx| {
9227 view.add_selection_above(&AddSelectionAbove, cx);
9228 assert_eq!(
9229 view.selections.display_ranges(cx),
9230 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9231 );
9232 });
9233
9234 view.update(cx, |view, cx| {
9235 view.change_selections(None, cx, |s| {
9236 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9237 });
9238 view.add_selection_below(&AddSelectionBelow, cx);
9239 assert_eq!(
9240 view.selections.display_ranges(cx),
9241 vec![
9242 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9243 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9244 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9245 ]
9246 );
9247 });
9248
9249 view.update(cx, |view, cx| {
9250 view.add_selection_below(&AddSelectionBelow, cx);
9251 assert_eq!(
9252 view.selections.display_ranges(cx),
9253 vec![
9254 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9255 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9256 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9257 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9258 ]
9259 );
9260 });
9261
9262 view.update(cx, |view, cx| {
9263 view.add_selection_above(&AddSelectionAbove, cx);
9264 assert_eq!(
9265 view.selections.display_ranges(cx),
9266 vec![
9267 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9268 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9269 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9270 ]
9271 );
9272 });
9273
9274 view.update(cx, |view, cx| {
9275 view.change_selections(None, cx, |s| {
9276 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9277 });
9278 });
9279 view.update(cx, |view, cx| {
9280 view.add_selection_above(&AddSelectionAbove, cx);
9281 assert_eq!(
9282 view.selections.display_ranges(cx),
9283 vec![
9284 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9285 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9286 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9287 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9288 ]
9289 );
9290 });
9291
9292 view.update(cx, |view, cx| {
9293 view.add_selection_below(&AddSelectionBelow, cx);
9294 assert_eq!(
9295 view.selections.display_ranges(cx),
9296 vec![
9297 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9298 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9299 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9300 ]
9301 );
9302 });
9303 }
9304
9305 #[gpui::test]
9306 async fn test_select_next(cx: &mut gpui::TestAppContext) {
9307 let mut cx = EditorTestContext::new(cx).await;
9308 cx.set_state("abc\nˇabc abc\ndefabc\nabc");
9309
9310 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9311 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9312
9313 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9314 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9315
9316 cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
9317 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9318
9319 cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
9320 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9321
9322 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9323 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9324
9325 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9326 cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9327 }
9328
9329 #[gpui::test]
9330 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9331 cx.update(|cx| cx.set_global(Settings::test(cx)));
9332 let language = Arc::new(Language::new(
9333 LanguageConfig::default(),
9334 Some(tree_sitter_rust::language()),
9335 ));
9336
9337 let text = r#"
9338 use mod1::mod2::{mod3, mod4};
9339
9340 fn fn_1(param1: bool, param2: &str) {
9341 let var1 = "text";
9342 }
9343 "#
9344 .unindent();
9345
9346 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9347 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9348 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9349 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9350 .await;
9351
9352 view.update(cx, |view, cx| {
9353 view.change_selections(None, cx, |s| {
9354 s.select_display_ranges([
9355 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9356 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9357 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9358 ]);
9359 });
9360 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9361 });
9362 assert_eq!(
9363 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9364 &[
9365 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9366 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9367 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9368 ]
9369 );
9370
9371 view.update(cx, |view, cx| {
9372 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9373 });
9374 assert_eq!(
9375 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9376 &[
9377 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9378 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9379 ]
9380 );
9381
9382 view.update(cx, |view, cx| {
9383 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9384 });
9385 assert_eq!(
9386 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9387 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9388 );
9389
9390 // Trying to expand the selected syntax node one more time has no effect.
9391 view.update(cx, |view, cx| {
9392 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9393 });
9394 assert_eq!(
9395 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9396 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9397 );
9398
9399 view.update(cx, |view, cx| {
9400 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9401 });
9402 assert_eq!(
9403 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9404 &[
9405 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9406 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9407 ]
9408 );
9409
9410 view.update(cx, |view, cx| {
9411 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, 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_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9424 });
9425 assert_eq!(
9426 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9427 &[
9428 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9429 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9430 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9431 ]
9432 );
9433
9434 // Trying to shrink the selected syntax node one more time has no effect.
9435 view.update(cx, |view, cx| {
9436 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9437 });
9438 assert_eq!(
9439 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9440 &[
9441 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9442 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9443 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9444 ]
9445 );
9446
9447 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9448 // a fold.
9449 view.update(cx, |view, cx| {
9450 view.fold_ranges(
9451 vec![
9452 Point::new(0, 21)..Point::new(0, 24),
9453 Point::new(3, 20)..Point::new(3, 22),
9454 ],
9455 cx,
9456 );
9457 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9458 });
9459 assert_eq!(
9460 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9461 &[
9462 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9463 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9464 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9465 ]
9466 );
9467 }
9468
9469 #[gpui::test]
9470 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9471 cx.update(|cx| cx.set_global(Settings::test(cx)));
9472 let language = Arc::new(
9473 Language::new(
9474 LanguageConfig {
9475 brackets: vec![
9476 BracketPair {
9477 start: "{".to_string(),
9478 end: "}".to_string(),
9479 close: false,
9480 newline: true,
9481 },
9482 BracketPair {
9483 start: "(".to_string(),
9484 end: ")".to_string(),
9485 close: false,
9486 newline: true,
9487 },
9488 ],
9489 ..Default::default()
9490 },
9491 Some(tree_sitter_rust::language()),
9492 )
9493 .with_indents_query(
9494 r#"
9495 (_ "(" ")" @end) @indent
9496 (_ "{" "}" @end) @indent
9497 "#,
9498 )
9499 .unwrap(),
9500 );
9501
9502 let text = "fn a() {}";
9503
9504 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9505 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9506 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9507 editor
9508 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9509 .await;
9510
9511 editor.update(cx, |editor, cx| {
9512 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9513 editor.newline(&Newline, cx);
9514 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9515 assert_eq!(
9516 editor.selections.ranges(cx),
9517 &[
9518 Point::new(1, 4)..Point::new(1, 4),
9519 Point::new(3, 4)..Point::new(3, 4),
9520 Point::new(5, 0)..Point::new(5, 0)
9521 ]
9522 );
9523 });
9524 }
9525
9526 #[gpui::test]
9527 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9528 cx.update(|cx| cx.set_global(Settings::test(cx)));
9529 let language = Arc::new(Language::new(
9530 LanguageConfig {
9531 brackets: vec![
9532 BracketPair {
9533 start: "{".to_string(),
9534 end: "}".to_string(),
9535 close: true,
9536 newline: true,
9537 },
9538 BracketPair {
9539 start: "/*".to_string(),
9540 end: " */".to_string(),
9541 close: true,
9542 newline: true,
9543 },
9544 BracketPair {
9545 start: "[".to_string(),
9546 end: "]".to_string(),
9547 close: false,
9548 newline: true,
9549 },
9550 ],
9551 autoclose_before: "})]".to_string(),
9552 ..Default::default()
9553 },
9554 Some(tree_sitter_rust::language()),
9555 ));
9556
9557 let text = r#"
9558 a
9559
9560 /
9561
9562 "#
9563 .unindent();
9564
9565 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9566 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9567 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9568 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9569 .await;
9570
9571 view.update(cx, |view, cx| {
9572 view.change_selections(None, cx, |s| {
9573 s.select_display_ranges([
9574 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9575 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9576 ])
9577 });
9578
9579 view.handle_input("{", cx);
9580 view.handle_input("{", cx);
9581 view.handle_input("{", cx);
9582 assert_eq!(
9583 view.text(cx),
9584 "
9585 {{{}}}
9586 {{{}}}
9587 /
9588
9589 "
9590 .unindent()
9591 );
9592
9593 view.move_right(&MoveRight, cx);
9594 view.handle_input("}", cx);
9595 view.handle_input("}", cx);
9596 view.handle_input("}", cx);
9597 assert_eq!(
9598 view.text(cx),
9599 "
9600 {{{}}}}
9601 {{{}}}}
9602 /
9603
9604 "
9605 .unindent()
9606 );
9607
9608 view.undo(&Undo, cx);
9609 view.handle_input("/", cx);
9610 view.handle_input("*", cx);
9611 assert_eq!(
9612 view.text(cx),
9613 "
9614 /* */
9615 /* */
9616 /
9617
9618 "
9619 .unindent()
9620 );
9621
9622 view.undo(&Undo, cx);
9623 view.change_selections(None, cx, |s| {
9624 s.select_display_ranges([
9625 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9626 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9627 ])
9628 });
9629 view.handle_input("*", cx);
9630 assert_eq!(
9631 view.text(cx),
9632 "
9633 a
9634
9635 /*
9636 *
9637 "
9638 .unindent()
9639 );
9640
9641 // Don't autoclose if the next character isn't whitespace and isn't
9642 // listed in the language's "autoclose_before" section.
9643 view.finalize_last_transaction(cx);
9644 view.change_selections(None, cx, |s| {
9645 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9646 });
9647 view.handle_input("{", cx);
9648 assert_eq!(
9649 view.text(cx),
9650 "
9651 {a
9652
9653 /*
9654 *
9655 "
9656 .unindent()
9657 );
9658
9659 view.undo(&Undo, cx);
9660 view.change_selections(None, cx, |s| {
9661 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9662 });
9663 view.handle_input("{", cx);
9664 assert_eq!(
9665 view.text(cx),
9666 "
9667 {a}
9668
9669 /*
9670 *
9671 "
9672 .unindent()
9673 );
9674 assert_eq!(
9675 view.selections.display_ranges(cx),
9676 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9677 );
9678
9679 view.undo(&Undo, cx);
9680 view.handle_input("[", cx);
9681 assert_eq!(
9682 view.text(cx),
9683 "
9684 [a]
9685
9686 /*
9687 *
9688 "
9689 .unindent()
9690 );
9691 assert_eq!(
9692 view.selections.display_ranges(cx),
9693 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9694 );
9695
9696 view.undo(&Undo, cx);
9697 view.change_selections(None, cx, |s| {
9698 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9699 });
9700 view.handle_input("[", cx);
9701 assert_eq!(
9702 view.text(cx),
9703 "
9704 a[
9705
9706 /*
9707 *
9708 "
9709 .unindent()
9710 );
9711 assert_eq!(
9712 view.selections.display_ranges(cx),
9713 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9714 );
9715 });
9716 }
9717
9718 #[gpui::test]
9719 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9720 cx.update(|cx| cx.set_global(Settings::test(cx)));
9721 let language = Arc::new(Language::new(
9722 LanguageConfig {
9723 brackets: vec![BracketPair {
9724 start: "{".to_string(),
9725 end: "}".to_string(),
9726 close: true,
9727 newline: true,
9728 }],
9729 ..Default::default()
9730 },
9731 Some(tree_sitter_rust::language()),
9732 ));
9733
9734 let text = r#"
9735 a
9736 b
9737 c
9738 "#
9739 .unindent();
9740
9741 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9742 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9743 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9744 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9745 .await;
9746
9747 view.update(cx, |view, cx| {
9748 view.change_selections(None, cx, |s| {
9749 s.select_display_ranges([
9750 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9751 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9752 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9753 ])
9754 });
9755
9756 view.handle_input("{", cx);
9757 view.handle_input("{", cx);
9758 view.handle_input("{", cx);
9759 assert_eq!(
9760 view.text(cx),
9761 "
9762 {{{a}}}
9763 {{{b}}}
9764 {{{c}}}
9765 "
9766 .unindent()
9767 );
9768 assert_eq!(
9769 view.selections.display_ranges(cx),
9770 [
9771 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9772 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9773 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9774 ]
9775 );
9776
9777 view.undo(&Undo, cx);
9778 assert_eq!(
9779 view.text(cx),
9780 "
9781 a
9782 b
9783 c
9784 "
9785 .unindent()
9786 );
9787 assert_eq!(
9788 view.selections.display_ranges(cx),
9789 [
9790 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9791 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9792 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9793 ]
9794 );
9795 });
9796 }
9797
9798 #[gpui::test]
9799 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9800 cx.update(|cx| cx.set_global(Settings::test(cx)));
9801 let language = Arc::new(Language::new(
9802 LanguageConfig {
9803 brackets: vec![BracketPair {
9804 start: "{".to_string(),
9805 end: "}".to_string(),
9806 close: true,
9807 newline: true,
9808 }],
9809 autoclose_before: "}".to_string(),
9810 ..Default::default()
9811 },
9812 Some(tree_sitter_rust::language()),
9813 ));
9814
9815 let text = r#"
9816 a
9817 b
9818 c
9819 "#
9820 .unindent();
9821
9822 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9823 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9824 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9825 editor
9826 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9827 .await;
9828
9829 editor.update(cx, |editor, cx| {
9830 editor.change_selections(None, cx, |s| {
9831 s.select_ranges([
9832 Point::new(0, 1)..Point::new(0, 1),
9833 Point::new(1, 1)..Point::new(1, 1),
9834 Point::new(2, 1)..Point::new(2, 1),
9835 ])
9836 });
9837
9838 editor.handle_input("{", cx);
9839 editor.handle_input("{", cx);
9840 editor.handle_input("_", cx);
9841 assert_eq!(
9842 editor.text(cx),
9843 "
9844 a{{_}}
9845 b{{_}}
9846 c{{_}}
9847 "
9848 .unindent()
9849 );
9850 assert_eq!(
9851 editor.selections.ranges::<Point>(cx),
9852 [
9853 Point::new(0, 4)..Point::new(0, 4),
9854 Point::new(1, 4)..Point::new(1, 4),
9855 Point::new(2, 4)..Point::new(2, 4)
9856 ]
9857 );
9858
9859 editor.backspace(&Default::default(), cx);
9860 editor.backspace(&Default::default(), cx);
9861 assert_eq!(
9862 editor.text(cx),
9863 "
9864 a{}
9865 b{}
9866 c{}
9867 "
9868 .unindent()
9869 );
9870 assert_eq!(
9871 editor.selections.ranges::<Point>(cx),
9872 [
9873 Point::new(0, 2)..Point::new(0, 2),
9874 Point::new(1, 2)..Point::new(1, 2),
9875 Point::new(2, 2)..Point::new(2, 2)
9876 ]
9877 );
9878
9879 editor.delete_to_previous_word_start(&Default::default(), cx);
9880 assert_eq!(
9881 editor.text(cx),
9882 "
9883 a
9884 b
9885 c
9886 "
9887 .unindent()
9888 );
9889 assert_eq!(
9890 editor.selections.ranges::<Point>(cx),
9891 [
9892 Point::new(0, 1)..Point::new(0, 1),
9893 Point::new(1, 1)..Point::new(1, 1),
9894 Point::new(2, 1)..Point::new(2, 1)
9895 ]
9896 );
9897 });
9898 }
9899
9900 #[gpui::test]
9901 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9902 cx.update(|cx| cx.set_global(Settings::test(cx)));
9903
9904 let (text, insertion_ranges) = parse_marked_text(
9905 indoc! {"
9906 a.ˇ b
9907 a.ˇ b
9908 a.ˇ b
9909 "},
9910 false,
9911 )
9912 .unwrap();
9913
9914 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9915 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9916
9917 editor.update(cx, |editor, cx| {
9918 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9919
9920 editor
9921 .insert_snippet(&insertion_ranges, snippet, cx)
9922 .unwrap();
9923
9924 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9925 let (expected_text, selection_ranges) =
9926 parse_marked_text(marked_text_ranges, false).unwrap();
9927 assert_eq!(editor.text(cx), expected_text);
9928 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9929 }
9930
9931 assert(
9932 editor,
9933 cx,
9934 indoc! {"
9935 a.f(«one», two, «three») b
9936 a.f(«one», two, «three») b
9937 a.f(«one», two, «three») b
9938 "},
9939 );
9940
9941 // Can't move earlier than the first tab stop
9942 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9943 assert(
9944 editor,
9945 cx,
9946 indoc! {"
9947 a.f(«one», two, «three») b
9948 a.f(«one», two, «three») b
9949 a.f(«one», two, «three») b
9950 "},
9951 );
9952
9953 assert!(editor.move_to_next_snippet_tabstop(cx));
9954 assert(
9955 editor,
9956 cx,
9957 indoc! {"
9958 a.f(one, «two», three) b
9959 a.f(one, «two», three) b
9960 a.f(one, «two», three) b
9961 "},
9962 );
9963
9964 editor.move_to_prev_snippet_tabstop(cx);
9965 assert(
9966 editor,
9967 cx,
9968 indoc! {"
9969 a.f(«one», two, «three») b
9970 a.f(«one», two, «three») b
9971 a.f(«one», two, «three») b
9972 "},
9973 );
9974
9975 assert!(editor.move_to_next_snippet_tabstop(cx));
9976 assert(
9977 editor,
9978 cx,
9979 indoc! {"
9980 a.f(one, «two», three) b
9981 a.f(one, «two», three) b
9982 a.f(one, «two», three) b
9983 "},
9984 );
9985 assert!(editor.move_to_next_snippet_tabstop(cx));
9986 assert(
9987 editor,
9988 cx,
9989 indoc! {"
9990 a.f(one, two, three)ˇ b
9991 a.f(one, two, three)ˇ b
9992 a.f(one, two, three)ˇ b
9993 "},
9994 );
9995
9996 // As soon as the last tab stop is reached, snippet state is gone
9997 editor.move_to_prev_snippet_tabstop(cx);
9998 assert(
9999 editor,
10000 cx,
10001 indoc! {"
10002 a.f(one, two, three)ˇ b
10003 a.f(one, two, three)ˇ b
10004 a.f(one, two, three)ˇ b
10005 "},
10006 );
10007 });
10008 }
10009
10010 #[gpui::test]
10011 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
10012 cx.foreground().forbid_parking();
10013
10014 let mut language = Language::new(
10015 LanguageConfig {
10016 name: "Rust".into(),
10017 path_suffixes: vec!["rs".to_string()],
10018 ..Default::default()
10019 },
10020 Some(tree_sitter_rust::language()),
10021 );
10022 let mut fake_servers = language
10023 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10024 capabilities: lsp::ServerCapabilities {
10025 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10026 ..Default::default()
10027 },
10028 ..Default::default()
10029 }))
10030 .await;
10031
10032 let fs = FakeFs::new(cx.background().clone());
10033 fs.insert_file("/file.rs", Default::default()).await;
10034
10035 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10036 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10037 let buffer = project
10038 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10039 .await
10040 .unwrap();
10041
10042 cx.foreground().start_waiting();
10043 let fake_server = fake_servers.next().await.unwrap();
10044
10045 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10046 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10047 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10048 assert!(cx.read(|cx| editor.is_dirty(cx)));
10049
10050 let save = cx.update(|cx| editor.save(project.clone(), cx));
10051 fake_server
10052 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10053 assert_eq!(
10054 params.text_document.uri,
10055 lsp::Url::from_file_path("/file.rs").unwrap()
10056 );
10057 assert_eq!(params.options.tab_size, 4);
10058 Ok(Some(vec![lsp::TextEdit::new(
10059 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10060 ", ".to_string(),
10061 )]))
10062 })
10063 .next()
10064 .await;
10065 cx.foreground().start_waiting();
10066 save.await.unwrap();
10067 assert_eq!(
10068 editor.read_with(cx, |editor, cx| editor.text(cx)),
10069 "one, two\nthree\n"
10070 );
10071 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10072
10073 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10074 assert!(cx.read(|cx| editor.is_dirty(cx)));
10075
10076 // Ensure we can still save even if formatting hangs.
10077 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10078 assert_eq!(
10079 params.text_document.uri,
10080 lsp::Url::from_file_path("/file.rs").unwrap()
10081 );
10082 futures::future::pending::<()>().await;
10083 unreachable!()
10084 });
10085 let save = cx.update(|cx| editor.save(project.clone(), cx));
10086 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10087 cx.foreground().start_waiting();
10088 save.await.unwrap();
10089 assert_eq!(
10090 editor.read_with(cx, |editor, cx| editor.text(cx)),
10091 "one\ntwo\nthree\n"
10092 );
10093 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10094
10095 // Set rust language override and assert overriden tabsize is sent to language server
10096 cx.update(|cx| {
10097 cx.update_global::<Settings, _, _>(|settings, _| {
10098 settings.language_overrides.insert(
10099 "Rust".into(),
10100 EditorSettings {
10101 tab_size: Some(8.try_into().unwrap()),
10102 ..Default::default()
10103 },
10104 );
10105 })
10106 });
10107
10108 let save = cx.update(|cx| editor.save(project.clone(), cx));
10109 fake_server
10110 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10111 assert_eq!(
10112 params.text_document.uri,
10113 lsp::Url::from_file_path("/file.rs").unwrap()
10114 );
10115 assert_eq!(params.options.tab_size, 8);
10116 Ok(Some(vec![]))
10117 })
10118 .next()
10119 .await;
10120 cx.foreground().start_waiting();
10121 save.await.unwrap();
10122 }
10123
10124 #[gpui::test]
10125 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10126 cx.foreground().forbid_parking();
10127
10128 let mut language = Language::new(
10129 LanguageConfig {
10130 name: "Rust".into(),
10131 path_suffixes: vec!["rs".to_string()],
10132 ..Default::default()
10133 },
10134 Some(tree_sitter_rust::language()),
10135 );
10136 let mut fake_servers = language
10137 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10138 capabilities: lsp::ServerCapabilities {
10139 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10140 ..Default::default()
10141 },
10142 ..Default::default()
10143 }))
10144 .await;
10145
10146 let fs = FakeFs::new(cx.background().clone());
10147 fs.insert_file("/file.rs", Default::default()).await;
10148
10149 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10150 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10151 let buffer = project
10152 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10153 .await
10154 .unwrap();
10155
10156 cx.foreground().start_waiting();
10157 let fake_server = fake_servers.next().await.unwrap();
10158
10159 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10160 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10161 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10162 assert!(cx.read(|cx| editor.is_dirty(cx)));
10163
10164 let save = cx.update(|cx| editor.save(project.clone(), cx));
10165 fake_server
10166 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10167 assert_eq!(
10168 params.text_document.uri,
10169 lsp::Url::from_file_path("/file.rs").unwrap()
10170 );
10171 assert_eq!(params.options.tab_size, 4);
10172 Ok(Some(vec![lsp::TextEdit::new(
10173 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10174 ", ".to_string(),
10175 )]))
10176 })
10177 .next()
10178 .await;
10179 cx.foreground().start_waiting();
10180 save.await.unwrap();
10181 assert_eq!(
10182 editor.read_with(cx, |editor, cx| editor.text(cx)),
10183 "one, two\nthree\n"
10184 );
10185 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10186
10187 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10188 assert!(cx.read(|cx| editor.is_dirty(cx)));
10189
10190 // Ensure we can still save even if formatting hangs.
10191 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10192 move |params, _| async move {
10193 assert_eq!(
10194 params.text_document.uri,
10195 lsp::Url::from_file_path("/file.rs").unwrap()
10196 );
10197 futures::future::pending::<()>().await;
10198 unreachable!()
10199 },
10200 );
10201 let save = cx.update(|cx| editor.save(project.clone(), cx));
10202 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10203 cx.foreground().start_waiting();
10204 save.await.unwrap();
10205 assert_eq!(
10206 editor.read_with(cx, |editor, cx| editor.text(cx)),
10207 "one\ntwo\nthree\n"
10208 );
10209 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10210
10211 // Set rust language override and assert overriden tabsize is sent to language server
10212 cx.update(|cx| {
10213 cx.update_global::<Settings, _, _>(|settings, _| {
10214 settings.language_overrides.insert(
10215 "Rust".into(),
10216 EditorSettings {
10217 tab_size: Some(8.try_into().unwrap()),
10218 ..Default::default()
10219 },
10220 );
10221 })
10222 });
10223
10224 let save = cx.update(|cx| editor.save(project.clone(), cx));
10225 fake_server
10226 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10227 assert_eq!(
10228 params.text_document.uri,
10229 lsp::Url::from_file_path("/file.rs").unwrap()
10230 );
10231 assert_eq!(params.options.tab_size, 8);
10232 Ok(Some(vec![]))
10233 })
10234 .next()
10235 .await;
10236 cx.foreground().start_waiting();
10237 save.await.unwrap();
10238 }
10239
10240 #[gpui::test]
10241 async fn test_completion(cx: &mut gpui::TestAppContext) {
10242 let mut cx = EditorLspTestContext::new_rust(
10243 lsp::ServerCapabilities {
10244 completion_provider: Some(lsp::CompletionOptions {
10245 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10246 ..Default::default()
10247 }),
10248 ..Default::default()
10249 },
10250 cx,
10251 )
10252 .await;
10253
10254 cx.set_state(indoc! {"
10255 oneˇ
10256 two
10257 three
10258 "});
10259 cx.simulate_keystroke(".");
10260 handle_completion_request(
10261 &mut cx,
10262 indoc! {"
10263 one.|<>
10264 two
10265 three
10266 "},
10267 vec!["first_completion", "second_completion"],
10268 )
10269 .await;
10270 cx.condition(|editor, _| editor.context_menu_visible())
10271 .await;
10272 let apply_additional_edits = cx.update_editor(|editor, cx| {
10273 editor.move_down(&MoveDown, cx);
10274 editor
10275 .confirm_completion(&ConfirmCompletion::default(), cx)
10276 .unwrap()
10277 });
10278 cx.assert_editor_state(indoc! {"
10279 one.second_completionˇ
10280 two
10281 three
10282 "});
10283
10284 handle_resolve_completion_request(
10285 &mut cx,
10286 Some((
10287 indoc! {"
10288 one.second_completion
10289 two
10290 threeˇ
10291 "},
10292 "\nadditional edit",
10293 )),
10294 )
10295 .await;
10296 apply_additional_edits.await.unwrap();
10297 cx.assert_editor_state(indoc! {"
10298 one.second_completionˇ
10299 two
10300 three
10301 additional edit
10302 "});
10303
10304 cx.set_state(indoc! {"
10305 one.second_completion
10306 twoˇ
10307 threeˇ
10308 additional edit
10309 "});
10310 cx.simulate_keystroke(" ");
10311 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10312 cx.simulate_keystroke("s");
10313 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10314
10315 cx.assert_editor_state(indoc! {"
10316 one.second_completion
10317 two sˇ
10318 three sˇ
10319 additional edit
10320 "});
10321 handle_completion_request(
10322 &mut cx,
10323 indoc! {"
10324 one.second_completion
10325 two s
10326 three <s|>
10327 additional edit
10328 "},
10329 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10330 )
10331 .await;
10332 cx.condition(|editor, _| editor.context_menu_visible())
10333 .await;
10334
10335 cx.simulate_keystroke("i");
10336
10337 handle_completion_request(
10338 &mut cx,
10339 indoc! {"
10340 one.second_completion
10341 two si
10342 three <si|>
10343 additional edit
10344 "},
10345 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10346 )
10347 .await;
10348 cx.condition(|editor, _| editor.context_menu_visible())
10349 .await;
10350
10351 let apply_additional_edits = cx.update_editor(|editor, cx| {
10352 editor
10353 .confirm_completion(&ConfirmCompletion::default(), cx)
10354 .unwrap()
10355 });
10356 cx.assert_editor_state(indoc! {"
10357 one.second_completion
10358 two sixth_completionˇ
10359 three sixth_completionˇ
10360 additional edit
10361 "});
10362
10363 handle_resolve_completion_request(&mut cx, None).await;
10364 apply_additional_edits.await.unwrap();
10365
10366 cx.update(|cx| {
10367 cx.update_global::<Settings, _, _>(|settings, _| {
10368 settings.show_completions_on_input = false;
10369 })
10370 });
10371 cx.set_state("editorˇ");
10372 cx.simulate_keystroke(".");
10373 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10374 cx.simulate_keystroke("c");
10375 cx.simulate_keystroke("l");
10376 cx.simulate_keystroke("o");
10377 cx.assert_editor_state("editor.cloˇ");
10378 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10379 cx.update_editor(|editor, cx| {
10380 editor.show_completions(&ShowCompletions, cx);
10381 });
10382 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10383 cx.condition(|editor, _| editor.context_menu_visible())
10384 .await;
10385 let apply_additional_edits = cx.update_editor(|editor, cx| {
10386 editor
10387 .confirm_completion(&ConfirmCompletion::default(), cx)
10388 .unwrap()
10389 });
10390 cx.assert_editor_state("editor.closeˇ");
10391 handle_resolve_completion_request(&mut cx, None).await;
10392 apply_additional_edits.await.unwrap();
10393
10394 // Handle completion request passing a marked string specifying where the completion
10395 // should be triggered from using '|' character, what range should be replaced, and what completions
10396 // should be returned using '<' and '>' to delimit the range
10397 async fn handle_completion_request<'a>(
10398 cx: &mut EditorLspTestContext<'a>,
10399 marked_string: &str,
10400 completions: Vec<&'static str>,
10401 ) {
10402 let complete_from_marker: TextRangeMarker = '|'.into();
10403 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10404 let (_, mut marked_ranges) = marked_text_ranges_by(
10405 marked_string,
10406 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10407 );
10408
10409 let complete_from_position =
10410 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10411 let replace_range =
10412 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10413
10414 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10415 let completions = completions.clone();
10416 async move {
10417 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10418 assert_eq!(
10419 params.text_document_position.position,
10420 complete_from_position
10421 );
10422 Ok(Some(lsp::CompletionResponse::Array(
10423 completions
10424 .iter()
10425 .map(|completion_text| lsp::CompletionItem {
10426 label: completion_text.to_string(),
10427 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10428 range: replace_range.clone(),
10429 new_text: completion_text.to_string(),
10430 })),
10431 ..Default::default()
10432 })
10433 .collect(),
10434 )))
10435 }
10436 })
10437 .next()
10438 .await;
10439 }
10440
10441 async fn handle_resolve_completion_request<'a>(
10442 cx: &mut EditorLspTestContext<'a>,
10443 edit: Option<(&'static str, &'static str)>,
10444 ) {
10445 let edit = edit.map(|(marked_string, new_text)| {
10446 let (_, marked_ranges) = parse_marked_text(marked_string, false).unwrap();
10447 let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
10448 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10449 });
10450
10451 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10452 let edit = edit.clone();
10453 async move {
10454 Ok(lsp::CompletionItem {
10455 additional_text_edits: edit,
10456 ..Default::default()
10457 })
10458 }
10459 })
10460 .next()
10461 .await;
10462 }
10463 }
10464
10465 #[gpui::test]
10466 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10467 cx.update(|cx| cx.set_global(Settings::test(cx)));
10468 let language = Arc::new(Language::new(
10469 LanguageConfig {
10470 line_comment: Some("// ".to_string()),
10471 ..Default::default()
10472 },
10473 Some(tree_sitter_rust::language()),
10474 ));
10475
10476 let text = "
10477 fn a() {
10478 //b();
10479 // c();
10480 // d();
10481 }
10482 "
10483 .unindent();
10484
10485 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10486 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10487 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10488
10489 view.update(cx, |editor, cx| {
10490 // If multiple selections intersect a line, the line is only
10491 // toggled once.
10492 editor.change_selections(None, cx, |s| {
10493 s.select_display_ranges([
10494 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10495 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10496 ])
10497 });
10498 editor.toggle_comments(&ToggleComments, cx);
10499 assert_eq!(
10500 editor.text(cx),
10501 "
10502 fn a() {
10503 b();
10504 c();
10505 d();
10506 }
10507 "
10508 .unindent()
10509 );
10510
10511 // The comment prefix is inserted at the same column for every line
10512 // in a selection.
10513 editor.change_selections(None, cx, |s| {
10514 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10515 });
10516 editor.toggle_comments(&ToggleComments, cx);
10517 assert_eq!(
10518 editor.text(cx),
10519 "
10520 fn a() {
10521 // b();
10522 // c();
10523 // d();
10524 }
10525 "
10526 .unindent()
10527 );
10528
10529 // If a selection ends at the beginning of a line, that line is not toggled.
10530 editor.change_selections(None, cx, |s| {
10531 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10532 });
10533 editor.toggle_comments(&ToggleComments, cx);
10534 assert_eq!(
10535 editor.text(cx),
10536 "
10537 fn a() {
10538 // b();
10539 c();
10540 // d();
10541 }
10542 "
10543 .unindent()
10544 );
10545 });
10546 }
10547
10548 #[gpui::test]
10549 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10550 cx.set_global(Settings::test(cx));
10551 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10552 let multibuffer = cx.add_model(|cx| {
10553 let mut multibuffer = MultiBuffer::new(0);
10554 multibuffer.push_excerpts(
10555 buffer.clone(),
10556 [
10557 ExcerptRange {
10558 context: Point::new(0, 0)..Point::new(0, 4),
10559 primary: None,
10560 },
10561 ExcerptRange {
10562 context: Point::new(1, 0)..Point::new(1, 4),
10563 primary: None,
10564 },
10565 ],
10566 cx,
10567 );
10568 multibuffer
10569 });
10570
10571 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10572
10573 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10574 view.update(cx, |view, cx| {
10575 assert_eq!(view.text(cx), "aaaa\nbbbb");
10576 view.change_selections(None, cx, |s| {
10577 s.select_ranges([
10578 Point::new(0, 0)..Point::new(0, 0),
10579 Point::new(1, 0)..Point::new(1, 0),
10580 ])
10581 });
10582
10583 view.handle_input("X", cx);
10584 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10585 assert_eq!(
10586 view.selections.ranges(cx),
10587 [
10588 Point::new(0, 1)..Point::new(0, 1),
10589 Point::new(1, 1)..Point::new(1, 1),
10590 ]
10591 )
10592 });
10593 }
10594
10595 #[gpui::test]
10596 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10597 cx.set_global(Settings::test(cx));
10598 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10599 [aaaa
10600 (bbbb]
10601 cccc)"});
10602 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10603 context,
10604 primary: None,
10605 });
10606 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10607 let multibuffer = cx.add_model(|cx| {
10608 let mut multibuffer = MultiBuffer::new(0);
10609 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10610 multibuffer
10611 });
10612
10613 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10614 view.update(cx, |view, cx| {
10615 let (expected_text, selection_ranges) = parse_marked_text(
10616 indoc! {"
10617 aaaa
10618 bˇbbb
10619 bˇbbˇb
10620 cccc"
10621 },
10622 true,
10623 )
10624 .unwrap();
10625 assert_eq!(view.text(cx), expected_text);
10626 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10627
10628 view.handle_input("X", cx);
10629
10630 let (expected_text, expected_selections) = parse_marked_text(
10631 indoc! {"
10632 aaaa
10633 bXˇbbXb
10634 bXˇbbXˇb
10635 cccc"
10636 },
10637 false,
10638 )
10639 .unwrap();
10640 assert_eq!(view.text(cx), expected_text);
10641 assert_eq!(view.selections.ranges(cx), expected_selections);
10642
10643 view.newline(&Newline, cx);
10644 let (expected_text, expected_selections) = parse_marked_text(
10645 indoc! {"
10646 aaaa
10647 bX
10648 ˇbbX
10649 b
10650 bX
10651 ˇbbX
10652 ˇb
10653 cccc"
10654 },
10655 false,
10656 )
10657 .unwrap();
10658 assert_eq!(view.text(cx), expected_text);
10659 assert_eq!(view.selections.ranges(cx), expected_selections);
10660 });
10661 }
10662
10663 #[gpui::test]
10664 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10665 cx.set_global(Settings::test(cx));
10666 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10667 let mut excerpt1_id = None;
10668 let multibuffer = cx.add_model(|cx| {
10669 let mut multibuffer = MultiBuffer::new(0);
10670 excerpt1_id = multibuffer
10671 .push_excerpts(
10672 buffer.clone(),
10673 [
10674 ExcerptRange {
10675 context: Point::new(0, 0)..Point::new(1, 4),
10676 primary: None,
10677 },
10678 ExcerptRange {
10679 context: Point::new(1, 0)..Point::new(2, 4),
10680 primary: None,
10681 },
10682 ],
10683 cx,
10684 )
10685 .into_iter()
10686 .next();
10687 multibuffer
10688 });
10689 assert_eq!(
10690 multibuffer.read(cx).read(cx).text(),
10691 "aaaa\nbbbb\nbbbb\ncccc"
10692 );
10693 let (_, editor) = cx.add_window(Default::default(), |cx| {
10694 let mut editor = build_editor(multibuffer.clone(), cx);
10695 let snapshot = editor.snapshot(cx);
10696 editor.change_selections(None, cx, |s| {
10697 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10698 });
10699 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10700 assert_eq!(
10701 editor.selections.ranges(cx),
10702 [
10703 Point::new(1, 3)..Point::new(1, 3),
10704 Point::new(2, 1)..Point::new(2, 1),
10705 ]
10706 );
10707 editor
10708 });
10709
10710 // Refreshing selections is a no-op when excerpts haven't changed.
10711 editor.update(cx, |editor, cx| {
10712 editor.change_selections(None, cx, |s| {
10713 s.refresh();
10714 });
10715 assert_eq!(
10716 editor.selections.ranges(cx),
10717 [
10718 Point::new(1, 3)..Point::new(1, 3),
10719 Point::new(2, 1)..Point::new(2, 1),
10720 ]
10721 );
10722 });
10723
10724 multibuffer.update(cx, |multibuffer, cx| {
10725 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10726 });
10727 editor.update(cx, |editor, cx| {
10728 // Removing an excerpt causes the first selection to become degenerate.
10729 assert_eq!(
10730 editor.selections.ranges(cx),
10731 [
10732 Point::new(0, 0)..Point::new(0, 0),
10733 Point::new(0, 1)..Point::new(0, 1)
10734 ]
10735 );
10736
10737 // Refreshing selections will relocate the first selection to the original buffer
10738 // location.
10739 editor.change_selections(None, cx, |s| {
10740 s.refresh();
10741 });
10742 assert_eq!(
10743 editor.selections.ranges(cx),
10744 [
10745 Point::new(0, 1)..Point::new(0, 1),
10746 Point::new(0, 3)..Point::new(0, 3)
10747 ]
10748 );
10749 assert!(editor.selections.pending_anchor().is_some());
10750 });
10751 }
10752
10753 #[gpui::test]
10754 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10755 cx.set_global(Settings::test(cx));
10756 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10757 let mut excerpt1_id = None;
10758 let multibuffer = cx.add_model(|cx| {
10759 let mut multibuffer = MultiBuffer::new(0);
10760 excerpt1_id = multibuffer
10761 .push_excerpts(
10762 buffer.clone(),
10763 [
10764 ExcerptRange {
10765 context: Point::new(0, 0)..Point::new(1, 4),
10766 primary: None,
10767 },
10768 ExcerptRange {
10769 context: Point::new(1, 0)..Point::new(2, 4),
10770 primary: None,
10771 },
10772 ],
10773 cx,
10774 )
10775 .into_iter()
10776 .next();
10777 multibuffer
10778 });
10779 assert_eq!(
10780 multibuffer.read(cx).read(cx).text(),
10781 "aaaa\nbbbb\nbbbb\ncccc"
10782 );
10783 let (_, editor) = cx.add_window(Default::default(), |cx| {
10784 let mut editor = build_editor(multibuffer.clone(), cx);
10785 let snapshot = editor.snapshot(cx);
10786 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10787 assert_eq!(
10788 editor.selections.ranges(cx),
10789 [Point::new(1, 3)..Point::new(1, 3)]
10790 );
10791 editor
10792 });
10793
10794 multibuffer.update(cx, |multibuffer, cx| {
10795 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10796 });
10797 editor.update(cx, |editor, cx| {
10798 assert_eq!(
10799 editor.selections.ranges(cx),
10800 [Point::new(0, 0)..Point::new(0, 0)]
10801 );
10802
10803 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10804 editor.change_selections(None, cx, |s| {
10805 s.refresh();
10806 });
10807 assert_eq!(
10808 editor.selections.ranges(cx),
10809 [Point::new(0, 3)..Point::new(0, 3)]
10810 );
10811 assert!(editor.selections.pending_anchor().is_some());
10812 });
10813 }
10814
10815 #[gpui::test]
10816 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10817 cx.update(|cx| cx.set_global(Settings::test(cx)));
10818 let language = Arc::new(
10819 Language::new(
10820 LanguageConfig {
10821 brackets: vec![
10822 BracketPair {
10823 start: "{".to_string(),
10824 end: "}".to_string(),
10825 close: true,
10826 newline: true,
10827 },
10828 BracketPair {
10829 start: "/* ".to_string(),
10830 end: " */".to_string(),
10831 close: true,
10832 newline: true,
10833 },
10834 ],
10835 ..Default::default()
10836 },
10837 Some(tree_sitter_rust::language()),
10838 )
10839 .with_indents_query("")
10840 .unwrap(),
10841 );
10842
10843 let text = concat!(
10844 "{ }\n", // Suppress rustfmt
10845 " x\n", //
10846 " /* */\n", //
10847 "x\n", //
10848 "{{} }\n", //
10849 );
10850
10851 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10852 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10853 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10854 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10855 .await;
10856
10857 view.update(cx, |view, cx| {
10858 view.change_selections(None, cx, |s| {
10859 s.select_display_ranges([
10860 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10861 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10862 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10863 ])
10864 });
10865 view.newline(&Newline, cx);
10866
10867 assert_eq!(
10868 view.buffer().read(cx).read(cx).text(),
10869 concat!(
10870 "{ \n", // Suppress rustfmt
10871 "\n", //
10872 "}\n", //
10873 " x\n", //
10874 " /* \n", //
10875 " \n", //
10876 " */\n", //
10877 "x\n", //
10878 "{{} \n", //
10879 "}\n", //
10880 )
10881 );
10882 });
10883 }
10884
10885 #[gpui::test]
10886 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10887 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10888
10889 cx.set_global(Settings::test(cx));
10890 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10891
10892 editor.update(cx, |editor, cx| {
10893 struct Type1;
10894 struct Type2;
10895
10896 let buffer = buffer.read(cx).snapshot(cx);
10897
10898 let anchor_range = |range: Range<Point>| {
10899 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10900 };
10901
10902 editor.highlight_background::<Type1>(
10903 vec![
10904 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10905 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10906 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10907 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10908 ],
10909 |_| Color::red(),
10910 cx,
10911 );
10912 editor.highlight_background::<Type2>(
10913 vec![
10914 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10915 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10916 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10917 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10918 ],
10919 |_| Color::green(),
10920 cx,
10921 );
10922
10923 let snapshot = editor.snapshot(cx);
10924 let mut highlighted_ranges = editor.background_highlights_in_range(
10925 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10926 &snapshot,
10927 cx.global::<Settings>().theme.as_ref(),
10928 );
10929 // Enforce a consistent ordering based on color without relying on the ordering of the
10930 // highlight's `TypeId` which is non-deterministic.
10931 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10932 assert_eq!(
10933 highlighted_ranges,
10934 &[
10935 (
10936 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10937 Color::green(),
10938 ),
10939 (
10940 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10941 Color::green(),
10942 ),
10943 (
10944 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10945 Color::red(),
10946 ),
10947 (
10948 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10949 Color::red(),
10950 ),
10951 ]
10952 );
10953 assert_eq!(
10954 editor.background_highlights_in_range(
10955 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10956 &snapshot,
10957 cx.global::<Settings>().theme.as_ref(),
10958 ),
10959 &[(
10960 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10961 Color::red(),
10962 )]
10963 );
10964 });
10965 }
10966
10967 #[gpui::test]
10968 fn test_following(cx: &mut gpui::MutableAppContext) {
10969 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10970
10971 cx.set_global(Settings::test(cx));
10972
10973 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10974 let (_, follower) = cx.add_window(
10975 WindowOptions {
10976 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10977 ..Default::default()
10978 },
10979 |cx| build_editor(buffer.clone(), cx),
10980 );
10981
10982 let pending_update = Rc::new(RefCell::new(None));
10983 follower.update(cx, {
10984 let update = pending_update.clone();
10985 |_, cx| {
10986 cx.subscribe(&leader, move |_, leader, event, cx| {
10987 leader
10988 .read(cx)
10989 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10990 })
10991 .detach();
10992 }
10993 });
10994
10995 // Update the selections only
10996 leader.update(cx, |leader, cx| {
10997 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10998 });
10999 follower.update(cx, |follower, cx| {
11000 follower
11001 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11002 .unwrap();
11003 });
11004 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
11005
11006 // Update the scroll position only
11007 leader.update(cx, |leader, cx| {
11008 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11009 });
11010 follower.update(cx, |follower, cx| {
11011 follower
11012 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11013 .unwrap();
11014 });
11015 assert_eq!(
11016 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
11017 vec2f(1.5, 3.5)
11018 );
11019
11020 // Update the selections and scroll position
11021 leader.update(cx, |leader, cx| {
11022 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
11023 leader.request_autoscroll(Autoscroll::Newest, cx);
11024 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11025 });
11026 follower.update(cx, |follower, cx| {
11027 let initial_scroll_position = follower.scroll_position(cx);
11028 follower
11029 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11030 .unwrap();
11031 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
11032 assert!(follower.autoscroll_request.is_some());
11033 });
11034 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
11035
11036 // Creating a pending selection that precedes another selection
11037 leader.update(cx, |leader, cx| {
11038 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11039 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
11040 });
11041 follower.update(cx, |follower, cx| {
11042 follower
11043 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11044 .unwrap();
11045 });
11046 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
11047
11048 // Extend the pending selection so that it surrounds another selection
11049 leader.update(cx, |leader, cx| {
11050 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
11051 });
11052 follower.update(cx, |follower, cx| {
11053 follower
11054 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11055 .unwrap();
11056 });
11057 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
11058 }
11059
11060 #[test]
11061 fn test_combine_syntax_and_fuzzy_match_highlights() {
11062 let string = "abcdefghijklmnop";
11063 let syntax_ranges = [
11064 (
11065 0..3,
11066 HighlightStyle {
11067 color: Some(Color::red()),
11068 ..Default::default()
11069 },
11070 ),
11071 (
11072 4..8,
11073 HighlightStyle {
11074 color: Some(Color::green()),
11075 ..Default::default()
11076 },
11077 ),
11078 ];
11079 let match_indices = [4, 6, 7, 8];
11080 assert_eq!(
11081 combine_syntax_and_fuzzy_match_highlights(
11082 &string,
11083 Default::default(),
11084 syntax_ranges.into_iter(),
11085 &match_indices,
11086 ),
11087 &[
11088 (
11089 0..3,
11090 HighlightStyle {
11091 color: Some(Color::red()),
11092 ..Default::default()
11093 },
11094 ),
11095 (
11096 4..5,
11097 HighlightStyle {
11098 color: Some(Color::green()),
11099 weight: Some(fonts::Weight::BOLD),
11100 ..Default::default()
11101 },
11102 ),
11103 (
11104 5..6,
11105 HighlightStyle {
11106 color: Some(Color::green()),
11107 ..Default::default()
11108 },
11109 ),
11110 (
11111 6..8,
11112 HighlightStyle {
11113 color: Some(Color::green()),
11114 weight: Some(fonts::Weight::BOLD),
11115 ..Default::default()
11116 },
11117 ),
11118 (
11119 8..9,
11120 HighlightStyle {
11121 weight: Some(fonts::Weight::BOLD),
11122 ..Default::default()
11123 },
11124 ),
11125 ]
11126 );
11127 }
11128
11129 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11130 let point = DisplayPoint::new(row as u32, column as u32);
11131 point..point
11132 }
11133
11134 fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11135 let (text, ranges) = parse_marked_text(marked_text, true).unwrap();
11136 assert_eq!(view.text(cx), text);
11137 assert_eq!(
11138 view.selections.ranges(cx),
11139 ranges,
11140 "Assert selections are {}",
11141 marked_text
11142 );
11143 }
11144}
11145
11146trait RangeExt<T> {
11147 fn sorted(&self) -> Range<T>;
11148 fn to_inclusive(&self) -> RangeInclusive<T>;
11149}
11150
11151impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11152 fn sorted(&self) -> Self {
11153 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11154 }
11155
11156 fn to_inclusive(&self) -> RangeInclusive<T> {
11157 self.start.clone()..=self.end.clone()
11158 }
11159}
11160
11161trait RangeToAnchorExt {
11162 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11163}
11164
11165impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11166 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11167 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11168 }
11169}