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