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