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