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 "});
8207 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8208 cx.assert_editor_state(indoc! {"
8209 «oneˇ» «twoˇ»
8210 three
8211 four
8212 "});
8213
8214 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8215 cx.assert_editor_state(indoc! {"
8216 «oneˇ» «twoˇ»
8217 three
8218 four
8219 "});
8220
8221 // select across line ending
8222 cx.set_state(indoc! {"
8223 one two
8224 t«hree
8225 ˇ» four
8226 "});
8227 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8228 cx.assert_editor_state(indoc! {"
8229 one two
8230 t«hree
8231 ˇ» four
8232 "});
8233
8234 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8235 cx.assert_editor_state(indoc! {"
8236 one two
8237 t«hree
8238 ˇ» four
8239 "});
8240
8241 // Ensure that indenting/outdenting works when the cursor is at column 0.
8242 cx.set_state(indoc! {"
8243 one two
8244 ˇthree
8245 four
8246 "});
8247 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8248 cx.assert_editor_state(indoc! {"
8249 one two
8250 ˇthree
8251 four
8252 "});
8253
8254 cx.set_state(indoc! {"
8255 one two
8256 ˇ three
8257 four
8258 "});
8259 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8260 cx.assert_editor_state(indoc! {"
8261 one two
8262 ˇthree
8263 four
8264 "});
8265 }
8266
8267 #[gpui::test]
8268 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8269 let mut cx = EditorTestContext::new(cx).await;
8270 cx.update(|cx| {
8271 cx.update_global::<Settings, _, _>(|settings, _| {
8272 settings.editor_overrides.hard_tabs = Some(true);
8273 });
8274 });
8275
8276 // select two ranges on one line
8277 cx.set_state(indoc! {"
8278 «oneˇ» «twoˇ»
8279 three
8280 four
8281 "});
8282 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8283 cx.assert_editor_state(indoc! {"
8284 \t«oneˇ» «twoˇ»
8285 three
8286 four
8287 "});
8288 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8289 cx.assert_editor_state(indoc! {"
8290 \t\t«oneˇ» «twoˇ»
8291 three
8292 four
8293 "});
8294 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8295 cx.assert_editor_state(indoc! {"
8296 \t«oneˇ» «twoˇ»
8297 three
8298 four
8299 "});
8300 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8301 cx.assert_editor_state(indoc! {"
8302 «oneˇ» «twoˇ»
8303 three
8304 four
8305 "});
8306
8307 // select across a line ending
8308 cx.set_state(indoc! {"
8309 one two
8310 t«hree
8311 ˇ»four
8312 "});
8313 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8314 cx.assert_editor_state(indoc! {"
8315 one two
8316 \tt«hree
8317 ˇ»four
8318 "});
8319 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8320 cx.assert_editor_state(indoc! {"
8321 one two
8322 \t\tt«hree
8323 ˇ»four
8324 "});
8325 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8326 cx.assert_editor_state(indoc! {"
8327 one two
8328 \tt«hree
8329 ˇ»four
8330 "});
8331 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8332 cx.assert_editor_state(indoc! {"
8333 one two
8334 t«hree
8335 ˇ»four
8336 "});
8337
8338 // Ensure that indenting/outdenting works when the cursor is at column 0.
8339 cx.set_state(indoc! {"
8340 one two
8341 ˇthree
8342 four
8343 "});
8344 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8345 cx.assert_editor_state(indoc! {"
8346 one two
8347 ˇthree
8348 four
8349 "});
8350 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8351 cx.assert_editor_state(indoc! {"
8352 one two
8353 \tˇthree
8354 four
8355 "});
8356 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8357 cx.assert_editor_state(indoc! {"
8358 one two
8359 ˇthree
8360 four
8361 "});
8362 }
8363
8364 #[gpui::test]
8365 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8366 cx.set_global(
8367 Settings::test(cx)
8368 .with_language_defaults(
8369 "TOML",
8370 EditorSettings {
8371 tab_size: Some(2.try_into().unwrap()),
8372 ..Default::default()
8373 },
8374 )
8375 .with_language_defaults(
8376 "Rust",
8377 EditorSettings {
8378 tab_size: Some(4.try_into().unwrap()),
8379 ..Default::default()
8380 },
8381 ),
8382 );
8383 let toml_language = Arc::new(Language::new(
8384 LanguageConfig {
8385 name: "TOML".into(),
8386 ..Default::default()
8387 },
8388 None,
8389 ));
8390 let rust_language = Arc::new(Language::new(
8391 LanguageConfig {
8392 name: "Rust".into(),
8393 ..Default::default()
8394 },
8395 None,
8396 ));
8397
8398 let toml_buffer = cx
8399 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8400 let rust_buffer = cx.add_model(|cx| {
8401 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8402 });
8403 let multibuffer = cx.add_model(|cx| {
8404 let mut multibuffer = MultiBuffer::new(0);
8405 multibuffer.push_excerpts(
8406 toml_buffer.clone(),
8407 [ExcerptRange {
8408 context: Point::new(0, 0)..Point::new(2, 0),
8409 primary: None,
8410 }],
8411 cx,
8412 );
8413 multibuffer.push_excerpts(
8414 rust_buffer.clone(),
8415 [ExcerptRange {
8416 context: Point::new(0, 0)..Point::new(1, 0),
8417 primary: None,
8418 }],
8419 cx,
8420 );
8421 multibuffer
8422 });
8423
8424 cx.add_window(Default::default(), |cx| {
8425 let mut editor = build_editor(multibuffer, cx);
8426
8427 assert_eq!(
8428 editor.text(cx),
8429 indoc! {"
8430 a = 1
8431 b = 2
8432
8433 const c: usize = 3;
8434 "}
8435 );
8436
8437 select_ranges(
8438 &mut editor,
8439 indoc! {"
8440 «aˇ» = 1
8441 b = 2
8442
8443 «const c:ˇ» usize = 3;
8444 "},
8445 cx,
8446 );
8447
8448 editor.tab(&Tab, cx);
8449 assert_text_with_selections(
8450 &mut editor,
8451 indoc! {"
8452 «aˇ» = 1
8453 b = 2
8454
8455 «const c:ˇ» usize = 3;
8456 "},
8457 cx,
8458 );
8459 editor.tab_prev(&TabPrev, cx);
8460 assert_text_with_selections(
8461 &mut editor,
8462 indoc! {"
8463 «aˇ» = 1
8464 b = 2
8465
8466 «const c:ˇ» usize = 3;
8467 "},
8468 cx,
8469 );
8470
8471 editor
8472 });
8473 }
8474
8475 #[gpui::test]
8476 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8477 let mut cx = EditorTestContext::new(cx).await;
8478
8479 // Basic backspace
8480 cx.set_state(indoc! {"
8481 onˇe two three
8482 fou«rˇ» five six
8483 seven «ˇeight nine
8484 »ten
8485 "});
8486 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8487 cx.assert_editor_state(indoc! {"
8488 oˇe two three
8489 fouˇ five six
8490 seven ˇten
8491 "});
8492
8493 // Test backspace inside and around indents
8494 cx.set_state(indoc! {"
8495 zero
8496 ˇone
8497 ˇtwo
8498 ˇ ˇ ˇ three
8499 ˇ ˇ four
8500 "});
8501 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8502 cx.assert_editor_state(indoc! {"
8503 zero
8504 ˇone
8505 ˇtwo
8506 ˇ threeˇ four
8507 "});
8508
8509 // Test backspace with line_mode set to true
8510 cx.update_editor(|e, _| e.selections.line_mode = true);
8511 cx.set_state(indoc! {"
8512 The ˇquick ˇbrown
8513 fox jumps over
8514 the lazy dog
8515 ˇThe qu«ick bˇ»rown"});
8516 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8517 cx.assert_editor_state(indoc! {"
8518 ˇfox jumps over
8519 the lazy dogˇ"});
8520 }
8521
8522 #[gpui::test]
8523 async fn test_delete(cx: &mut gpui::TestAppContext) {
8524 let mut cx = EditorTestContext::new(cx).await;
8525
8526 cx.set_state(indoc! {"
8527 onˇe two three
8528 fou«rˇ» five six
8529 seven «ˇeight nine
8530 »ten
8531 "});
8532 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8533 cx.assert_editor_state(indoc! {"
8534 onˇ two three
8535 fouˇ five six
8536 seven ˇten
8537 "});
8538
8539 // Test backspace with line_mode set to true
8540 cx.update_editor(|e, _| e.selections.line_mode = true);
8541 cx.set_state(indoc! {"
8542 The ˇquick ˇbrown
8543 fox «ˇjum»ps over
8544 the lazy dog
8545 ˇThe qu«ick bˇ»rown"});
8546 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8547 cx.assert_editor_state("ˇthe lazy dogˇ");
8548 }
8549
8550 #[gpui::test]
8551 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8552 cx.set_global(Settings::test(cx));
8553 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8554 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8555 view.update(cx, |view, cx| {
8556 view.change_selections(None, cx, |s| {
8557 s.select_display_ranges([
8558 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8559 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8560 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8561 ])
8562 });
8563 view.delete_line(&DeleteLine, cx);
8564 assert_eq!(view.display_text(cx), "ghi");
8565 assert_eq!(
8566 view.selections.display_ranges(cx),
8567 vec![
8568 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8569 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8570 ]
8571 );
8572 });
8573
8574 cx.set_global(Settings::test(cx));
8575 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8576 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8577 view.update(cx, |view, cx| {
8578 view.change_selections(None, cx, |s| {
8579 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8580 });
8581 view.delete_line(&DeleteLine, cx);
8582 assert_eq!(view.display_text(cx), "ghi\n");
8583 assert_eq!(
8584 view.selections.display_ranges(cx),
8585 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8586 );
8587 });
8588 }
8589
8590 #[gpui::test]
8591 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8592 cx.set_global(Settings::test(cx));
8593 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8594 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8595 view.update(cx, |view, cx| {
8596 view.change_selections(None, cx, |s| {
8597 s.select_display_ranges([
8598 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8599 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8600 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8601 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8602 ])
8603 });
8604 view.duplicate_line(&DuplicateLine, cx);
8605 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8606 assert_eq!(
8607 view.selections.display_ranges(cx),
8608 vec![
8609 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8610 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8611 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8612 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8613 ]
8614 );
8615 });
8616
8617 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8618 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8619 view.update(cx, |view, cx| {
8620 view.change_selections(None, cx, |s| {
8621 s.select_display_ranges([
8622 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8623 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8624 ])
8625 });
8626 view.duplicate_line(&DuplicateLine, cx);
8627 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8628 assert_eq!(
8629 view.selections.display_ranges(cx),
8630 vec![
8631 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8632 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8633 ]
8634 );
8635 });
8636 }
8637
8638 #[gpui::test]
8639 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8640 cx.set_global(Settings::test(cx));
8641 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8642 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8643 view.update(cx, |view, cx| {
8644 view.fold_ranges(
8645 vec![
8646 Point::new(0, 2)..Point::new(1, 2),
8647 Point::new(2, 3)..Point::new(4, 1),
8648 Point::new(7, 0)..Point::new(8, 4),
8649 ],
8650 cx,
8651 );
8652 view.change_selections(None, cx, |s| {
8653 s.select_display_ranges([
8654 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8655 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8656 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8657 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8658 ])
8659 });
8660 assert_eq!(
8661 view.display_text(cx),
8662 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8663 );
8664
8665 view.move_line_up(&MoveLineUp, cx);
8666 assert_eq!(
8667 view.display_text(cx),
8668 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8669 );
8670 assert_eq!(
8671 view.selections.display_ranges(cx),
8672 vec![
8673 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8674 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8675 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8676 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8677 ]
8678 );
8679 });
8680
8681 view.update(cx, |view, cx| {
8682 view.move_line_down(&MoveLineDown, cx);
8683 assert_eq!(
8684 view.display_text(cx),
8685 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8686 );
8687 assert_eq!(
8688 view.selections.display_ranges(cx),
8689 vec![
8690 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8691 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8692 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8693 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8694 ]
8695 );
8696 });
8697
8698 view.update(cx, |view, cx| {
8699 view.move_line_down(&MoveLineDown, cx);
8700 assert_eq!(
8701 view.display_text(cx),
8702 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8703 );
8704 assert_eq!(
8705 view.selections.display_ranges(cx),
8706 vec![
8707 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8708 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8709 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8710 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8711 ]
8712 );
8713 });
8714
8715 view.update(cx, |view, cx| {
8716 view.move_line_up(&MoveLineUp, cx);
8717 assert_eq!(
8718 view.display_text(cx),
8719 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8720 );
8721 assert_eq!(
8722 view.selections.display_ranges(cx),
8723 vec![
8724 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8725 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8726 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8727 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8728 ]
8729 );
8730 });
8731 }
8732
8733 #[gpui::test]
8734 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8735 cx.set_global(Settings::test(cx));
8736 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8737 let snapshot = buffer.read(cx).snapshot(cx);
8738 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8739 editor.update(cx, |editor, cx| {
8740 editor.insert_blocks(
8741 [BlockProperties {
8742 style: BlockStyle::Fixed,
8743 position: snapshot.anchor_after(Point::new(2, 0)),
8744 disposition: BlockDisposition::Below,
8745 height: 1,
8746 render: Arc::new(|_| Empty::new().boxed()),
8747 }],
8748 cx,
8749 );
8750 editor.change_selections(None, cx, |s| {
8751 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8752 });
8753 editor.move_line_down(&MoveLineDown, cx);
8754 });
8755 }
8756
8757 #[gpui::test]
8758 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8759 cx.set_global(Settings::test(cx));
8760
8761 cx.add_window(Default::default(), |cx| {
8762 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8763
8764 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8765 editor.transpose(&Default::default(), cx);
8766 assert_eq!(editor.text(cx), "bac");
8767 assert_eq!(editor.selections.ranges(cx), [2..2]);
8768
8769 editor.transpose(&Default::default(), cx);
8770 assert_eq!(editor.text(cx), "bca");
8771 assert_eq!(editor.selections.ranges(cx), [3..3]);
8772
8773 editor.transpose(&Default::default(), cx);
8774 assert_eq!(editor.text(cx), "bac");
8775 assert_eq!(editor.selections.ranges(cx), [3..3]);
8776
8777 editor
8778 })
8779 .1;
8780
8781 cx.add_window(Default::default(), |cx| {
8782 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8783
8784 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8785 editor.transpose(&Default::default(), cx);
8786 assert_eq!(editor.text(cx), "acb\nde");
8787 assert_eq!(editor.selections.ranges(cx), [3..3]);
8788
8789 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8790 editor.transpose(&Default::default(), cx);
8791 assert_eq!(editor.text(cx), "acbd\ne");
8792 assert_eq!(editor.selections.ranges(cx), [5..5]);
8793
8794 editor.transpose(&Default::default(), cx);
8795 assert_eq!(editor.text(cx), "acbde\n");
8796 assert_eq!(editor.selections.ranges(cx), [6..6]);
8797
8798 editor.transpose(&Default::default(), cx);
8799 assert_eq!(editor.text(cx), "acbd\ne");
8800 assert_eq!(editor.selections.ranges(cx), [6..6]);
8801
8802 editor
8803 })
8804 .1;
8805
8806 cx.add_window(Default::default(), |cx| {
8807 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8808
8809 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8810 editor.transpose(&Default::default(), cx);
8811 assert_eq!(editor.text(cx), "bacd\ne");
8812 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8813
8814 editor.transpose(&Default::default(), cx);
8815 assert_eq!(editor.text(cx), "bcade\n");
8816 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8817
8818 editor.transpose(&Default::default(), cx);
8819 assert_eq!(editor.text(cx), "bcda\ne");
8820 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8821
8822 editor.transpose(&Default::default(), cx);
8823 assert_eq!(editor.text(cx), "bcade\n");
8824 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8825
8826 editor.transpose(&Default::default(), cx);
8827 assert_eq!(editor.text(cx), "bcaed\n");
8828 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8829
8830 editor
8831 })
8832 .1;
8833
8834 cx.add_window(Default::default(), |cx| {
8835 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8836
8837 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8838 editor.transpose(&Default::default(), cx);
8839 assert_eq!(editor.text(cx), "🏀🍐✋");
8840 assert_eq!(editor.selections.ranges(cx), [8..8]);
8841
8842 editor.transpose(&Default::default(), cx);
8843 assert_eq!(editor.text(cx), "🏀✋🍐");
8844 assert_eq!(editor.selections.ranges(cx), [11..11]);
8845
8846 editor.transpose(&Default::default(), cx);
8847 assert_eq!(editor.text(cx), "🏀🍐✋");
8848 assert_eq!(editor.selections.ranges(cx), [11..11]);
8849
8850 editor
8851 })
8852 .1;
8853 }
8854
8855 #[gpui::test]
8856 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8857 let mut cx = EditorTestContext::new(cx).await;
8858
8859 cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
8860 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8861 cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
8862
8863 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8864 cx.set_state("two ˇfour ˇsix ˇ");
8865 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8866 cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
8867
8868 // Paste again but with only two cursors. Since the number of cursors doesn't
8869 // match the number of slices in the clipboard, the entire clipboard text
8870 // is pasted at each cursor.
8871 cx.set_state("ˇtwo one✅ four three six five ˇ");
8872 cx.update_editor(|e, cx| {
8873 e.handle_input("( ", cx);
8874 e.paste(&Paste, cx);
8875 e.handle_input(") ", cx);
8876 });
8877 cx.assert_editor_state(indoc! {"
8878 ( one✅
8879 three
8880 five ) ˇtwo one✅ four three six five ( one✅
8881 three
8882 five ) ˇ"});
8883
8884 // Cut with three selections, one of which is full-line.
8885 cx.set_state(indoc! {"
8886 1«2ˇ»3
8887 4ˇ567
8888 «8ˇ»9"});
8889 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8890 cx.assert_editor_state(indoc! {"
8891 1ˇ3
8892 ˇ9"});
8893
8894 // Paste with three selections, noticing how the copied selection that was full-line
8895 // gets inserted before the second cursor.
8896 cx.set_state(indoc! {"
8897 1ˇ3
8898 9ˇ
8899 «oˇ»ne"});
8900 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8901 cx.assert_editor_state(indoc! {"
8902 12ˇ3
8903 4567
8904 9ˇ
8905 8ˇne"});
8906
8907 // Copy with a single cursor only, which writes the whole line into the clipboard.
8908 cx.set_state(indoc! {"
8909 The quick brown
8910 fox juˇmps over
8911 the lazy dog"});
8912 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8913 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8914
8915 // Paste with three selections, noticing how the copied full-line selection is inserted
8916 // before the empty selections but replaces the selection that is non-empty.
8917 cx.set_state(indoc! {"
8918 Tˇhe quick brown
8919 «foˇ»x jumps over
8920 tˇhe lazy dog"});
8921 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8922 cx.assert_editor_state(indoc! {"
8923 fox jumps over
8924 Tˇhe quick brown
8925 fox jumps over
8926 ˇx jumps over
8927 fox jumps over
8928 tˇhe lazy dog"});
8929 }
8930
8931 #[gpui::test]
8932 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
8933 let mut cx = EditorTestContext::new(cx).await;
8934 let language = Arc::new(Language::new(
8935 LanguageConfig::default(),
8936 Some(tree_sitter_rust::language()),
8937 ));
8938 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8939
8940 // Cut an indented block, without the leading whitespace.
8941 cx.set_state(indoc! {"
8942 const a: B = (
8943 c(),
8944 «d(
8945 e,
8946 f
8947 )ˇ»
8948 );
8949 "});
8950 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8951 cx.assert_editor_state(indoc! {"
8952 const a: B = (
8953 c(),
8954 ˇ
8955 );
8956 "});
8957
8958 // Paste it at the same position.
8959 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8960 cx.assert_editor_state(indoc! {"
8961 const a: B = (
8962 c(),
8963 d(
8964 e,
8965 f
8966 )ˇ
8967 );
8968 "});
8969
8970 // Paste it at a line with a lower indent level.
8971 cx.set_state(indoc! {"
8972 ˇ
8973 const a: B = (
8974 c(),
8975 );
8976 "});
8977 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8978 cx.assert_editor_state(indoc! {"
8979 d(
8980 e,
8981 f
8982 )ˇ
8983 const a: B = (
8984 c(),
8985 );
8986 "});
8987
8988 // Cut an indented block, with the leading whitespace.
8989 cx.set_state(indoc! {"
8990 const a: B = (
8991 c(),
8992 « d(
8993 e,
8994 f
8995 )
8996 ˇ»);
8997 "});
8998 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8999 cx.assert_editor_state(indoc! {"
9000 const a: B = (
9001 c(),
9002 ˇ);
9003 "});
9004
9005 // Paste it at the same position.
9006 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9007 cx.assert_editor_state(indoc! {"
9008 const a: B = (
9009 c(),
9010 d(
9011 e,
9012 f
9013 )
9014 ˇ);
9015 "});
9016
9017 // Paste it at a line with a higher indent level.
9018 cx.set_state(indoc! {"
9019 const a: B = (
9020 c(),
9021 d(
9022 e,
9023 fˇ
9024 )
9025 );
9026 "});
9027 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9028 cx.assert_editor_state(indoc! {"
9029 const a: B = (
9030 c(),
9031 d(
9032 e,
9033 f d(
9034 e,
9035 f
9036 )
9037 ˇ
9038 )
9039 );
9040 "});
9041 }
9042
9043 #[gpui::test]
9044 fn test_select_all(cx: &mut gpui::MutableAppContext) {
9045 cx.set_global(Settings::test(cx));
9046 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
9047 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9048 view.update(cx, |view, cx| {
9049 view.select_all(&SelectAll, cx);
9050 assert_eq!(
9051 view.selections.display_ranges(cx),
9052 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
9053 );
9054 });
9055 }
9056
9057 #[gpui::test]
9058 fn test_select_line(cx: &mut gpui::MutableAppContext) {
9059 cx.set_global(Settings::test(cx));
9060 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
9061 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9062 view.update(cx, |view, cx| {
9063 view.change_selections(None, cx, |s| {
9064 s.select_display_ranges([
9065 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9066 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9067 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9068 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
9069 ])
9070 });
9071 view.select_line(&SelectLine, cx);
9072 assert_eq!(
9073 view.selections.display_ranges(cx),
9074 vec![
9075 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
9076 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
9077 ]
9078 );
9079 });
9080
9081 view.update(cx, |view, cx| {
9082 view.select_line(&SelectLine, cx);
9083 assert_eq!(
9084 view.selections.display_ranges(cx),
9085 vec![
9086 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
9087 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
9088 ]
9089 );
9090 });
9091
9092 view.update(cx, |view, cx| {
9093 view.select_line(&SelectLine, cx);
9094 assert_eq!(
9095 view.selections.display_ranges(cx),
9096 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
9097 );
9098 });
9099 }
9100
9101 #[gpui::test]
9102 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
9103 cx.set_global(Settings::test(cx));
9104 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
9105 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9106 view.update(cx, |view, cx| {
9107 view.fold_ranges(
9108 vec![
9109 Point::new(0, 2)..Point::new(1, 2),
9110 Point::new(2, 3)..Point::new(4, 1),
9111 Point::new(7, 0)..Point::new(8, 4),
9112 ],
9113 cx,
9114 );
9115 view.change_selections(None, cx, |s| {
9116 s.select_display_ranges([
9117 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9118 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9119 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9120 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9121 ])
9122 });
9123 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
9124 });
9125
9126 view.update(cx, |view, cx| {
9127 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9128 assert_eq!(
9129 view.display_text(cx),
9130 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9131 );
9132 assert_eq!(
9133 view.selections.display_ranges(cx),
9134 [
9135 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9136 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9137 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9138 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9139 ]
9140 );
9141 });
9142
9143 view.update(cx, |view, cx| {
9144 view.change_selections(None, cx, |s| {
9145 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9146 });
9147 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9148 assert_eq!(
9149 view.display_text(cx),
9150 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9151 );
9152 assert_eq!(
9153 view.selections.display_ranges(cx),
9154 [
9155 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9156 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9157 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9158 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9159 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9160 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9161 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9162 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9163 ]
9164 );
9165 });
9166 }
9167
9168 #[gpui::test]
9169 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9170 cx.set_global(Settings::test(cx));
9171 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9172 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9173
9174 view.update(cx, |view, cx| {
9175 view.change_selections(None, cx, |s| {
9176 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9177 });
9178 });
9179 view.update(cx, |view, cx| {
9180 view.add_selection_above(&AddSelectionAbove, cx);
9181 assert_eq!(
9182 view.selections.display_ranges(cx),
9183 vec![
9184 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9185 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9186 ]
9187 );
9188 });
9189
9190 view.update(cx, |view, cx| {
9191 view.add_selection_above(&AddSelectionAbove, cx);
9192 assert_eq!(
9193 view.selections.display_ranges(cx),
9194 vec![
9195 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9196 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9197 ]
9198 );
9199 });
9200
9201 view.update(cx, |view, cx| {
9202 view.add_selection_below(&AddSelectionBelow, cx);
9203 assert_eq!(
9204 view.selections.display_ranges(cx),
9205 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9206 );
9207
9208 view.undo_selection(&UndoSelection, cx);
9209 assert_eq!(
9210 view.selections.display_ranges(cx),
9211 vec![
9212 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9213 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9214 ]
9215 );
9216
9217 view.redo_selection(&RedoSelection, cx);
9218 assert_eq!(
9219 view.selections.display_ranges(cx),
9220 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9221 );
9222 });
9223
9224 view.update(cx, |view, cx| {
9225 view.add_selection_below(&AddSelectionBelow, cx);
9226 assert_eq!(
9227 view.selections.display_ranges(cx),
9228 vec![
9229 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9230 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9231 ]
9232 );
9233 });
9234
9235 view.update(cx, |view, cx| {
9236 view.add_selection_below(&AddSelectionBelow, cx);
9237 assert_eq!(
9238 view.selections.display_ranges(cx),
9239 vec![
9240 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9241 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9242 ]
9243 );
9244 });
9245
9246 view.update(cx, |view, cx| {
9247 view.change_selections(None, cx, |s| {
9248 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9249 });
9250 });
9251 view.update(cx, |view, cx| {
9252 view.add_selection_below(&AddSelectionBelow, cx);
9253 assert_eq!(
9254 view.selections.display_ranges(cx),
9255 vec![
9256 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9257 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9258 ]
9259 );
9260 });
9261
9262 view.update(cx, |view, cx| {
9263 view.add_selection_below(&AddSelectionBelow, cx);
9264 assert_eq!(
9265 view.selections.display_ranges(cx),
9266 vec![
9267 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9268 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9269 ]
9270 );
9271 });
9272
9273 view.update(cx, |view, cx| {
9274 view.add_selection_above(&AddSelectionAbove, cx);
9275 assert_eq!(
9276 view.selections.display_ranges(cx),
9277 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9278 );
9279 });
9280
9281 view.update(cx, |view, cx| {
9282 view.add_selection_above(&AddSelectionAbove, cx);
9283 assert_eq!(
9284 view.selections.display_ranges(cx),
9285 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9286 );
9287 });
9288
9289 view.update(cx, |view, cx| {
9290 view.change_selections(None, cx, |s| {
9291 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9292 });
9293 view.add_selection_below(&AddSelectionBelow, cx);
9294 assert_eq!(
9295 view.selections.display_ranges(cx),
9296 vec![
9297 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9298 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9299 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9300 ]
9301 );
9302 });
9303
9304 view.update(cx, |view, cx| {
9305 view.add_selection_below(&AddSelectionBelow, cx);
9306 assert_eq!(
9307 view.selections.display_ranges(cx),
9308 vec![
9309 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9310 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9311 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9312 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9313 ]
9314 );
9315 });
9316
9317 view.update(cx, |view, cx| {
9318 view.add_selection_above(&AddSelectionAbove, cx);
9319 assert_eq!(
9320 view.selections.display_ranges(cx),
9321 vec![
9322 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9323 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9324 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9325 ]
9326 );
9327 });
9328
9329 view.update(cx, |view, cx| {
9330 view.change_selections(None, cx, |s| {
9331 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9332 });
9333 });
9334 view.update(cx, |view, cx| {
9335 view.add_selection_above(&AddSelectionAbove, cx);
9336 assert_eq!(
9337 view.selections.display_ranges(cx),
9338 vec![
9339 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9340 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9341 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9342 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9343 ]
9344 );
9345 });
9346
9347 view.update(cx, |view, cx| {
9348 view.add_selection_below(&AddSelectionBelow, cx);
9349 assert_eq!(
9350 view.selections.display_ranges(cx),
9351 vec![
9352 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9353 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9354 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9355 ]
9356 );
9357 });
9358 }
9359
9360 #[gpui::test]
9361 fn test_select_next(cx: &mut gpui::MutableAppContext) {
9362 cx.set_global(Settings::test(cx));
9363
9364 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
9365 let buffer = MultiBuffer::build_simple(&text, cx);
9366 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9367
9368 view.update(cx, |view, cx| {
9369 view.change_selections(None, cx, |s| {
9370 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
9371 });
9372 view.select_next(
9373 &SelectNext {
9374 replace_newest: false,
9375 },
9376 cx,
9377 );
9378 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9379
9380 view.select_next(
9381 &SelectNext {
9382 replace_newest: false,
9383 },
9384 cx,
9385 );
9386 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9387
9388 view.undo_selection(&UndoSelection, cx);
9389 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
9390
9391 view.redo_selection(&RedoSelection, cx);
9392 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
9393
9394 view.select_next(
9395 &SelectNext {
9396 replace_newest: false,
9397 },
9398 cx,
9399 );
9400 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
9401
9402 view.select_next(
9403 &SelectNext {
9404 replace_newest: false,
9405 },
9406 cx,
9407 );
9408 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
9409 });
9410 }
9411
9412 #[gpui::test]
9413 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9414 cx.update(|cx| cx.set_global(Settings::test(cx)));
9415 let language = Arc::new(Language::new(
9416 LanguageConfig::default(),
9417 Some(tree_sitter_rust::language()),
9418 ));
9419
9420 let text = r#"
9421 use mod1::mod2::{mod3, mod4};
9422
9423 fn fn_1(param1: bool, param2: &str) {
9424 let var1 = "text";
9425 }
9426 "#
9427 .unindent();
9428
9429 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9430 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9431 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9432 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9433 .await;
9434
9435 view.update(cx, |view, cx| {
9436 view.change_selections(None, cx, |s| {
9437 s.select_display_ranges([
9438 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9439 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9440 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9441 ]);
9442 });
9443 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9444 });
9445 assert_eq!(
9446 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9447 &[
9448 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9449 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9450 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9451 ]
9452 );
9453
9454 view.update(cx, |view, cx| {
9455 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9456 });
9457 assert_eq!(
9458 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9459 &[
9460 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9461 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9462 ]
9463 );
9464
9465 view.update(cx, |view, cx| {
9466 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9467 });
9468 assert_eq!(
9469 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9470 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9471 );
9472
9473 // Trying to expand the selected syntax node one more time has no effect.
9474 view.update(cx, |view, cx| {
9475 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9476 });
9477 assert_eq!(
9478 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9479 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9480 );
9481
9482 view.update(cx, |view, cx| {
9483 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9484 });
9485 assert_eq!(
9486 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9487 &[
9488 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9489 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9490 ]
9491 );
9492
9493 view.update(cx, |view, cx| {
9494 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9495 });
9496 assert_eq!(
9497 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9498 &[
9499 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9500 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9501 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9502 ]
9503 );
9504
9505 view.update(cx, |view, cx| {
9506 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9507 });
9508 assert_eq!(
9509 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9510 &[
9511 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9512 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9513 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9514 ]
9515 );
9516
9517 // Trying to shrink the selected syntax node one more time has no effect.
9518 view.update(cx, |view, cx| {
9519 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9520 });
9521 assert_eq!(
9522 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9523 &[
9524 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9525 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9526 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9527 ]
9528 );
9529
9530 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9531 // a fold.
9532 view.update(cx, |view, cx| {
9533 view.fold_ranges(
9534 vec![
9535 Point::new(0, 21)..Point::new(0, 24),
9536 Point::new(3, 20)..Point::new(3, 22),
9537 ],
9538 cx,
9539 );
9540 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9541 });
9542 assert_eq!(
9543 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9544 &[
9545 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9546 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9547 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9548 ]
9549 );
9550 }
9551
9552 #[gpui::test]
9553 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9554 cx.update(|cx| cx.set_global(Settings::test(cx)));
9555 let language = Arc::new(
9556 Language::new(
9557 LanguageConfig {
9558 brackets: vec![
9559 BracketPair {
9560 start: "{".to_string(),
9561 end: "}".to_string(),
9562 close: false,
9563 newline: true,
9564 },
9565 BracketPair {
9566 start: "(".to_string(),
9567 end: ")".to_string(),
9568 close: false,
9569 newline: true,
9570 },
9571 ],
9572 ..Default::default()
9573 },
9574 Some(tree_sitter_rust::language()),
9575 )
9576 .with_indents_query(
9577 r#"
9578 (_ "(" ")" @end) @indent
9579 (_ "{" "}" @end) @indent
9580 "#,
9581 )
9582 .unwrap(),
9583 );
9584
9585 let text = "fn a() {}";
9586
9587 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9588 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9589 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9590 editor
9591 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9592 .await;
9593
9594 editor.update(cx, |editor, cx| {
9595 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9596 editor.newline(&Newline, cx);
9597 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9598 assert_eq!(
9599 editor.selections.ranges(cx),
9600 &[
9601 Point::new(1, 4)..Point::new(1, 4),
9602 Point::new(3, 4)..Point::new(3, 4),
9603 Point::new(5, 0)..Point::new(5, 0)
9604 ]
9605 );
9606 });
9607 }
9608
9609 #[gpui::test]
9610 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9611 cx.update(|cx| cx.set_global(Settings::test(cx)));
9612 let language = Arc::new(Language::new(
9613 LanguageConfig {
9614 brackets: vec![
9615 BracketPair {
9616 start: "{".to_string(),
9617 end: "}".to_string(),
9618 close: true,
9619 newline: true,
9620 },
9621 BracketPair {
9622 start: "/*".to_string(),
9623 end: " */".to_string(),
9624 close: true,
9625 newline: true,
9626 },
9627 BracketPair {
9628 start: "[".to_string(),
9629 end: "]".to_string(),
9630 close: false,
9631 newline: true,
9632 },
9633 ],
9634 autoclose_before: "})]".to_string(),
9635 ..Default::default()
9636 },
9637 Some(tree_sitter_rust::language()),
9638 ));
9639
9640 let text = r#"
9641 a
9642
9643 /
9644
9645 "#
9646 .unindent();
9647
9648 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9649 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9650 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9651 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9652 .await;
9653
9654 view.update(cx, |view, cx| {
9655 view.change_selections(None, cx, |s| {
9656 s.select_display_ranges([
9657 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9658 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9659 ])
9660 });
9661
9662 view.handle_input("{", cx);
9663 view.handle_input("{", cx);
9664 view.handle_input("{", cx);
9665 assert_eq!(
9666 view.text(cx),
9667 "
9668 {{{}}}
9669 {{{}}}
9670 /
9671
9672 "
9673 .unindent()
9674 );
9675
9676 view.move_right(&MoveRight, cx);
9677 view.handle_input("}", cx);
9678 view.handle_input("}", cx);
9679 view.handle_input("}", cx);
9680 assert_eq!(
9681 view.text(cx),
9682 "
9683 {{{}}}}
9684 {{{}}}}
9685 /
9686
9687 "
9688 .unindent()
9689 );
9690
9691 view.undo(&Undo, cx);
9692 view.handle_input("/", cx);
9693 view.handle_input("*", cx);
9694 assert_eq!(
9695 view.text(cx),
9696 "
9697 /* */
9698 /* */
9699 /
9700
9701 "
9702 .unindent()
9703 );
9704
9705 view.undo(&Undo, cx);
9706 view.change_selections(None, cx, |s| {
9707 s.select_display_ranges([
9708 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9709 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9710 ])
9711 });
9712 view.handle_input("*", cx);
9713 assert_eq!(
9714 view.text(cx),
9715 "
9716 a
9717
9718 /*
9719 *
9720 "
9721 .unindent()
9722 );
9723
9724 // Don't autoclose if the next character isn't whitespace and isn't
9725 // listed in the language's "autoclose_before" section.
9726 view.finalize_last_transaction(cx);
9727 view.change_selections(None, cx, |s| {
9728 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9729 });
9730 view.handle_input("{", cx);
9731 assert_eq!(
9732 view.text(cx),
9733 "
9734 {a
9735
9736 /*
9737 *
9738 "
9739 .unindent()
9740 );
9741
9742 view.undo(&Undo, cx);
9743 view.change_selections(None, cx, |s| {
9744 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9745 });
9746 view.handle_input("{", cx);
9747 assert_eq!(
9748 view.text(cx),
9749 "
9750 {a}
9751
9752 /*
9753 *
9754 "
9755 .unindent()
9756 );
9757 assert_eq!(
9758 view.selections.display_ranges(cx),
9759 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9760 );
9761
9762 view.undo(&Undo, cx);
9763 view.handle_input("[", cx);
9764 assert_eq!(
9765 view.text(cx),
9766 "
9767 [a]
9768
9769 /*
9770 *
9771 "
9772 .unindent()
9773 );
9774 assert_eq!(
9775 view.selections.display_ranges(cx),
9776 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9777 );
9778
9779 view.undo(&Undo, cx);
9780 view.change_selections(None, cx, |s| {
9781 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9782 });
9783 view.handle_input("[", cx);
9784 assert_eq!(
9785 view.text(cx),
9786 "
9787 a[
9788
9789 /*
9790 *
9791 "
9792 .unindent()
9793 );
9794 assert_eq!(
9795 view.selections.display_ranges(cx),
9796 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9797 );
9798 });
9799 }
9800
9801 #[gpui::test]
9802 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9803 cx.update(|cx| cx.set_global(Settings::test(cx)));
9804 let language = Arc::new(Language::new(
9805 LanguageConfig {
9806 brackets: vec![BracketPair {
9807 start: "{".to_string(),
9808 end: "}".to_string(),
9809 close: true,
9810 newline: true,
9811 }],
9812 ..Default::default()
9813 },
9814 Some(tree_sitter_rust::language()),
9815 ));
9816
9817 let text = r#"
9818 a
9819 b
9820 c
9821 "#
9822 .unindent();
9823
9824 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9825 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9826 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9827 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9828 .await;
9829
9830 view.update(cx, |view, cx| {
9831 view.change_selections(None, cx, |s| {
9832 s.select_display_ranges([
9833 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9834 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9835 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9836 ])
9837 });
9838
9839 view.handle_input("{", cx);
9840 view.handle_input("{", cx);
9841 view.handle_input("{", cx);
9842 assert_eq!(
9843 view.text(cx),
9844 "
9845 {{{a}}}
9846 {{{b}}}
9847 {{{c}}}
9848 "
9849 .unindent()
9850 );
9851 assert_eq!(
9852 view.selections.display_ranges(cx),
9853 [
9854 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9855 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9856 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9857 ]
9858 );
9859
9860 view.undo(&Undo, cx);
9861 assert_eq!(
9862 view.text(cx),
9863 "
9864 a
9865 b
9866 c
9867 "
9868 .unindent()
9869 );
9870 assert_eq!(
9871 view.selections.display_ranges(cx),
9872 [
9873 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9874 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9875 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9876 ]
9877 );
9878 });
9879 }
9880
9881 #[gpui::test]
9882 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9883 cx.update(|cx| cx.set_global(Settings::test(cx)));
9884 let language = Arc::new(Language::new(
9885 LanguageConfig {
9886 brackets: vec![BracketPair {
9887 start: "{".to_string(),
9888 end: "}".to_string(),
9889 close: true,
9890 newline: true,
9891 }],
9892 autoclose_before: "}".to_string(),
9893 ..Default::default()
9894 },
9895 Some(tree_sitter_rust::language()),
9896 ));
9897
9898 let text = r#"
9899 a
9900 b
9901 c
9902 "#
9903 .unindent();
9904
9905 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9906 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9907 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9908 editor
9909 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9910 .await;
9911
9912 editor.update(cx, |editor, cx| {
9913 editor.change_selections(None, cx, |s| {
9914 s.select_ranges([
9915 Point::new(0, 1)..Point::new(0, 1),
9916 Point::new(1, 1)..Point::new(1, 1),
9917 Point::new(2, 1)..Point::new(2, 1),
9918 ])
9919 });
9920
9921 editor.handle_input("{", cx);
9922 editor.handle_input("{", cx);
9923 editor.handle_input("_", cx);
9924 assert_eq!(
9925 editor.text(cx),
9926 "
9927 a{{_}}
9928 b{{_}}
9929 c{{_}}
9930 "
9931 .unindent()
9932 );
9933 assert_eq!(
9934 editor.selections.ranges::<Point>(cx),
9935 [
9936 Point::new(0, 4)..Point::new(0, 4),
9937 Point::new(1, 4)..Point::new(1, 4),
9938 Point::new(2, 4)..Point::new(2, 4)
9939 ]
9940 );
9941
9942 editor.backspace(&Default::default(), cx);
9943 editor.backspace(&Default::default(), cx);
9944 assert_eq!(
9945 editor.text(cx),
9946 "
9947 a{}
9948 b{}
9949 c{}
9950 "
9951 .unindent()
9952 );
9953 assert_eq!(
9954 editor.selections.ranges::<Point>(cx),
9955 [
9956 Point::new(0, 2)..Point::new(0, 2),
9957 Point::new(1, 2)..Point::new(1, 2),
9958 Point::new(2, 2)..Point::new(2, 2)
9959 ]
9960 );
9961
9962 editor.delete_to_previous_word_start(&Default::default(), cx);
9963 assert_eq!(
9964 editor.text(cx),
9965 "
9966 a
9967 b
9968 c
9969 "
9970 .unindent()
9971 );
9972 assert_eq!(
9973 editor.selections.ranges::<Point>(cx),
9974 [
9975 Point::new(0, 1)..Point::new(0, 1),
9976 Point::new(1, 1)..Point::new(1, 1),
9977 Point::new(2, 1)..Point::new(2, 1)
9978 ]
9979 );
9980 });
9981 }
9982
9983 #[gpui::test]
9984 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9985 cx.update(|cx| cx.set_global(Settings::test(cx)));
9986
9987 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9988 a.| b
9989 a.| b
9990 a.| b"});
9991 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9992 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9993
9994 editor.update(cx, |editor, cx| {
9995 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9996
9997 editor
9998 .insert_snippet(&insertion_ranges, snippet, cx)
9999 .unwrap();
10000
10001 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
10002 let range_markers = ('<', '>');
10003 let (expected_text, mut selection_ranges_lookup) =
10004 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
10005 let selection_ranges = selection_ranges_lookup
10006 .remove(&range_markers.into())
10007 .unwrap();
10008 assert_eq!(editor.text(cx), expected_text);
10009 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
10010 }
10011 assert(
10012 editor,
10013 cx,
10014 indoc! {"
10015 a.f(<one>, two, <three>) b
10016 a.f(<one>, two, <three>) b
10017 a.f(<one>, two, <three>) b"},
10018 );
10019
10020 // Can't move earlier than the first tab stop
10021 assert!(!editor.move_to_prev_snippet_tabstop(cx));
10022 assert(
10023 editor,
10024 cx,
10025 indoc! {"
10026 a.f(<one>, two, <three>) b
10027 a.f(<one>, two, <three>) b
10028 a.f(<one>, two, <three>) b"},
10029 );
10030
10031 assert!(editor.move_to_next_snippet_tabstop(cx));
10032 assert(
10033 editor,
10034 cx,
10035 indoc! {"
10036 a.f(one, <two>, three) b
10037 a.f(one, <two>, three) b
10038 a.f(one, <two>, three) b"},
10039 );
10040
10041 editor.move_to_prev_snippet_tabstop(cx);
10042 assert(
10043 editor,
10044 cx,
10045 indoc! {"
10046 a.f(<one>, two, <three>) b
10047 a.f(<one>, two, <three>) b
10048 a.f(<one>, two, <three>) b"},
10049 );
10050
10051 assert!(editor.move_to_next_snippet_tabstop(cx));
10052 assert(
10053 editor,
10054 cx,
10055 indoc! {"
10056 a.f(one, <two>, three) b
10057 a.f(one, <two>, three) b
10058 a.f(one, <two>, three) b"},
10059 );
10060 assert!(editor.move_to_next_snippet_tabstop(cx));
10061 assert(
10062 editor,
10063 cx,
10064 indoc! {"
10065 a.f(one, two, three)<> b
10066 a.f(one, two, three)<> b
10067 a.f(one, two, three)<> b"},
10068 );
10069
10070 // As soon as the last tab stop is reached, snippet state is gone
10071 editor.move_to_prev_snippet_tabstop(cx);
10072 assert(
10073 editor,
10074 cx,
10075 indoc! {"
10076 a.f(one, two, three)<> b
10077 a.f(one, two, three)<> b
10078 a.f(one, two, three)<> b"},
10079 );
10080 });
10081 }
10082
10083 #[gpui::test]
10084 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
10085 cx.foreground().forbid_parking();
10086
10087 let mut language = Language::new(
10088 LanguageConfig {
10089 name: "Rust".into(),
10090 path_suffixes: vec!["rs".to_string()],
10091 ..Default::default()
10092 },
10093 Some(tree_sitter_rust::language()),
10094 );
10095 let mut fake_servers = language
10096 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10097 capabilities: lsp::ServerCapabilities {
10098 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10099 ..Default::default()
10100 },
10101 ..Default::default()
10102 }))
10103 .await;
10104
10105 let fs = FakeFs::new(cx.background().clone());
10106 fs.insert_file("/file.rs", Default::default()).await;
10107
10108 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10109 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10110 let buffer = project
10111 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10112 .await
10113 .unwrap();
10114
10115 cx.foreground().start_waiting();
10116 let fake_server = fake_servers.next().await.unwrap();
10117
10118 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10119 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10120 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10121 assert!(cx.read(|cx| editor.is_dirty(cx)));
10122
10123 let save = cx.update(|cx| editor.save(project.clone(), cx));
10124 fake_server
10125 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10126 assert_eq!(
10127 params.text_document.uri,
10128 lsp::Url::from_file_path("/file.rs").unwrap()
10129 );
10130 assert_eq!(params.options.tab_size, 4);
10131 Ok(Some(vec![lsp::TextEdit::new(
10132 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10133 ", ".to_string(),
10134 )]))
10135 })
10136 .next()
10137 .await;
10138 cx.foreground().start_waiting();
10139 save.await.unwrap();
10140 assert_eq!(
10141 editor.read_with(cx, |editor, cx| editor.text(cx)),
10142 "one, two\nthree\n"
10143 );
10144 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10145
10146 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10147 assert!(cx.read(|cx| editor.is_dirty(cx)));
10148
10149 // Ensure we can still save even if formatting hangs.
10150 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10151 assert_eq!(
10152 params.text_document.uri,
10153 lsp::Url::from_file_path("/file.rs").unwrap()
10154 );
10155 futures::future::pending::<()>().await;
10156 unreachable!()
10157 });
10158 let save = cx.update(|cx| editor.save(project.clone(), cx));
10159 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10160 cx.foreground().start_waiting();
10161 save.await.unwrap();
10162 assert_eq!(
10163 editor.read_with(cx, |editor, cx| editor.text(cx)),
10164 "one\ntwo\nthree\n"
10165 );
10166 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10167
10168 // Set rust language override and assert overriden tabsize is sent to language server
10169 cx.update(|cx| {
10170 cx.update_global::<Settings, _, _>(|settings, _| {
10171 settings.language_overrides.insert(
10172 "Rust".into(),
10173 EditorSettings {
10174 tab_size: Some(8.try_into().unwrap()),
10175 ..Default::default()
10176 },
10177 );
10178 })
10179 });
10180
10181 let save = cx.update(|cx| editor.save(project.clone(), cx));
10182 fake_server
10183 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10184 assert_eq!(
10185 params.text_document.uri,
10186 lsp::Url::from_file_path("/file.rs").unwrap()
10187 );
10188 assert_eq!(params.options.tab_size, 8);
10189 Ok(Some(vec![]))
10190 })
10191 .next()
10192 .await;
10193 cx.foreground().start_waiting();
10194 save.await.unwrap();
10195 }
10196
10197 #[gpui::test]
10198 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10199 cx.foreground().forbid_parking();
10200
10201 let mut language = Language::new(
10202 LanguageConfig {
10203 name: "Rust".into(),
10204 path_suffixes: vec!["rs".to_string()],
10205 ..Default::default()
10206 },
10207 Some(tree_sitter_rust::language()),
10208 );
10209 let mut fake_servers = language
10210 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10211 capabilities: lsp::ServerCapabilities {
10212 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10213 ..Default::default()
10214 },
10215 ..Default::default()
10216 }))
10217 .await;
10218
10219 let fs = FakeFs::new(cx.background().clone());
10220 fs.insert_file("/file.rs", Default::default()).await;
10221
10222 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10223 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10224 let buffer = project
10225 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10226 .await
10227 .unwrap();
10228
10229 cx.foreground().start_waiting();
10230 let fake_server = fake_servers.next().await.unwrap();
10231
10232 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10233 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10234 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10235 assert!(cx.read(|cx| editor.is_dirty(cx)));
10236
10237 let save = cx.update(|cx| editor.save(project.clone(), cx));
10238 fake_server
10239 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10240 assert_eq!(
10241 params.text_document.uri,
10242 lsp::Url::from_file_path("/file.rs").unwrap()
10243 );
10244 assert_eq!(params.options.tab_size, 4);
10245 Ok(Some(vec![lsp::TextEdit::new(
10246 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10247 ", ".to_string(),
10248 )]))
10249 })
10250 .next()
10251 .await;
10252 cx.foreground().start_waiting();
10253 save.await.unwrap();
10254 assert_eq!(
10255 editor.read_with(cx, |editor, cx| editor.text(cx)),
10256 "one, two\nthree\n"
10257 );
10258 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10259
10260 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10261 assert!(cx.read(|cx| editor.is_dirty(cx)));
10262
10263 // Ensure we can still save even if formatting hangs.
10264 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10265 move |params, _| async move {
10266 assert_eq!(
10267 params.text_document.uri,
10268 lsp::Url::from_file_path("/file.rs").unwrap()
10269 );
10270 futures::future::pending::<()>().await;
10271 unreachable!()
10272 },
10273 );
10274 let save = cx.update(|cx| editor.save(project.clone(), cx));
10275 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
10276 cx.foreground().start_waiting();
10277 save.await.unwrap();
10278 assert_eq!(
10279 editor.read_with(cx, |editor, cx| editor.text(cx)),
10280 "one\ntwo\nthree\n"
10281 );
10282 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10283
10284 // Set rust language override and assert overriden tabsize is sent to language server
10285 cx.update(|cx| {
10286 cx.update_global::<Settings, _, _>(|settings, _| {
10287 settings.language_overrides.insert(
10288 "Rust".into(),
10289 EditorSettings {
10290 tab_size: Some(8.try_into().unwrap()),
10291 ..Default::default()
10292 },
10293 );
10294 })
10295 });
10296
10297 let save = cx.update(|cx| editor.save(project.clone(), cx));
10298 fake_server
10299 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10300 assert_eq!(
10301 params.text_document.uri,
10302 lsp::Url::from_file_path("/file.rs").unwrap()
10303 );
10304 assert_eq!(params.options.tab_size, 8);
10305 Ok(Some(vec![]))
10306 })
10307 .next()
10308 .await;
10309 cx.foreground().start_waiting();
10310 save.await.unwrap();
10311 }
10312
10313 #[gpui::test]
10314 async fn test_completion(cx: &mut gpui::TestAppContext) {
10315 let mut cx = EditorLspTestContext::new_rust(
10316 lsp::ServerCapabilities {
10317 completion_provider: Some(lsp::CompletionOptions {
10318 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10319 ..Default::default()
10320 }),
10321 ..Default::default()
10322 },
10323 cx,
10324 )
10325 .await;
10326
10327 cx.set_state(indoc! {"
10328 oneˇ
10329 two
10330 three
10331 "});
10332 cx.simulate_keystroke(".");
10333 handle_completion_request(
10334 &mut cx,
10335 indoc! {"
10336 one.|<>
10337 two
10338 three
10339 "},
10340 vec!["first_completion", "second_completion"],
10341 )
10342 .await;
10343 cx.condition(|editor, _| editor.context_menu_visible())
10344 .await;
10345 let apply_additional_edits = cx.update_editor(|editor, cx| {
10346 editor.move_down(&MoveDown, cx);
10347 editor
10348 .confirm_completion(&ConfirmCompletion::default(), cx)
10349 .unwrap()
10350 });
10351 cx.assert_editor_state(indoc! {"
10352 one.second_completionˇ
10353 two
10354 three
10355 "});
10356
10357 handle_resolve_completion_request(
10358 &mut cx,
10359 Some((
10360 indoc! {"
10361 one.second_completion
10362 two
10363 three<>
10364 "},
10365 "\nadditional edit",
10366 )),
10367 )
10368 .await;
10369 apply_additional_edits.await.unwrap();
10370 cx.assert_editor_state(indoc! {"
10371 one.second_completionˇ
10372 two
10373 three
10374 additional edit
10375 "});
10376
10377 cx.set_state(indoc! {"
10378 one.second_completion
10379 twoˇ
10380 threeˇ
10381 additional edit
10382 "});
10383 cx.simulate_keystroke(" ");
10384 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10385 cx.simulate_keystroke("s");
10386 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10387
10388 cx.assert_editor_state(indoc! {"
10389 one.second_completion
10390 two sˇ
10391 three sˇ
10392 additional edit
10393 "});
10394 handle_completion_request(
10395 &mut cx,
10396 indoc! {"
10397 one.second_completion
10398 two s
10399 three <s|>
10400 additional edit
10401 "},
10402 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10403 )
10404 .await;
10405 cx.condition(|editor, _| editor.context_menu_visible())
10406 .await;
10407
10408 cx.simulate_keystroke("i");
10409
10410 handle_completion_request(
10411 &mut cx,
10412 indoc! {"
10413 one.second_completion
10414 two si
10415 three <si|>
10416 additional edit
10417 "},
10418 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10419 )
10420 .await;
10421 cx.condition(|editor, _| editor.context_menu_visible())
10422 .await;
10423
10424 let apply_additional_edits = cx.update_editor(|editor, cx| {
10425 editor
10426 .confirm_completion(&ConfirmCompletion::default(), cx)
10427 .unwrap()
10428 });
10429 cx.assert_editor_state(indoc! {"
10430 one.second_completion
10431 two sixth_completionˇ
10432 three sixth_completionˇ
10433 additional edit
10434 "});
10435
10436 handle_resolve_completion_request(&mut cx, None).await;
10437 apply_additional_edits.await.unwrap();
10438
10439 cx.update(|cx| {
10440 cx.update_global::<Settings, _, _>(|settings, _| {
10441 settings.show_completions_on_input = false;
10442 })
10443 });
10444 cx.set_state("editorˇ");
10445 cx.simulate_keystroke(".");
10446 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10447 cx.simulate_keystroke("c");
10448 cx.simulate_keystroke("l");
10449 cx.simulate_keystroke("o");
10450 cx.assert_editor_state("editor.cloˇ");
10451 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10452 cx.update_editor(|editor, cx| {
10453 editor.show_completions(&ShowCompletions, cx);
10454 });
10455 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10456 cx.condition(|editor, _| editor.context_menu_visible())
10457 .await;
10458 let apply_additional_edits = cx.update_editor(|editor, cx| {
10459 editor
10460 .confirm_completion(&ConfirmCompletion::default(), cx)
10461 .unwrap()
10462 });
10463 cx.assert_editor_state("editor.closeˇ");
10464 handle_resolve_completion_request(&mut cx, None).await;
10465 apply_additional_edits.await.unwrap();
10466
10467 // Handle completion request passing a marked string specifying where the completion
10468 // should be triggered from using '|' character, what range should be replaced, and what completions
10469 // should be returned using '<' and '>' to delimit the range
10470 async fn handle_completion_request<'a>(
10471 cx: &mut EditorLspTestContext<'a>,
10472 marked_string: &str,
10473 completions: Vec<&'static str>,
10474 ) {
10475 let complete_from_marker: TextRangeMarker = '|'.into();
10476 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10477 let (_, mut marked_ranges) = marked_text_ranges_by(
10478 marked_string,
10479 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10480 );
10481
10482 let complete_from_position =
10483 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10484 let replace_range =
10485 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10486
10487 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10488 let completions = completions.clone();
10489 async move {
10490 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10491 assert_eq!(
10492 params.text_document_position.position,
10493 complete_from_position
10494 );
10495 Ok(Some(lsp::CompletionResponse::Array(
10496 completions
10497 .iter()
10498 .map(|completion_text| lsp::CompletionItem {
10499 label: completion_text.to_string(),
10500 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10501 range: replace_range.clone(),
10502 new_text: completion_text.to_string(),
10503 })),
10504 ..Default::default()
10505 })
10506 .collect(),
10507 )))
10508 }
10509 })
10510 .next()
10511 .await;
10512 }
10513
10514 async fn handle_resolve_completion_request<'a>(
10515 cx: &mut EditorLspTestContext<'a>,
10516 edit: Option<(&'static str, &'static str)>,
10517 ) {
10518 let edit = edit.map(|(marked_string, new_text)| {
10519 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10520 let (_, mut marked_ranges) =
10521 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
10522
10523 let replace_range = cx
10524 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10525
10526 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10527 });
10528
10529 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10530 let edit = edit.clone();
10531 async move {
10532 Ok(lsp::CompletionItem {
10533 additional_text_edits: edit,
10534 ..Default::default()
10535 })
10536 }
10537 })
10538 .next()
10539 .await;
10540 }
10541 }
10542
10543 #[gpui::test]
10544 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10545 cx.update(|cx| cx.set_global(Settings::test(cx)));
10546 let language = Arc::new(Language::new(
10547 LanguageConfig {
10548 line_comment: Some("// ".to_string()),
10549 ..Default::default()
10550 },
10551 Some(tree_sitter_rust::language()),
10552 ));
10553
10554 let text = "
10555 fn a() {
10556 //b();
10557 // c();
10558 // d();
10559 }
10560 "
10561 .unindent();
10562
10563 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10564 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10565 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10566
10567 view.update(cx, |editor, cx| {
10568 // If multiple selections intersect a line, the line is only
10569 // toggled once.
10570 editor.change_selections(None, cx, |s| {
10571 s.select_display_ranges([
10572 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10573 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10574 ])
10575 });
10576 editor.toggle_comments(&ToggleComments, cx);
10577 assert_eq!(
10578 editor.text(cx),
10579 "
10580 fn a() {
10581 b();
10582 c();
10583 d();
10584 }
10585 "
10586 .unindent()
10587 );
10588
10589 // The comment prefix is inserted at the same column for every line
10590 // in a selection.
10591 editor.change_selections(None, cx, |s| {
10592 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10593 });
10594 editor.toggle_comments(&ToggleComments, cx);
10595 assert_eq!(
10596 editor.text(cx),
10597 "
10598 fn a() {
10599 // b();
10600 // c();
10601 // d();
10602 }
10603 "
10604 .unindent()
10605 );
10606
10607 // If a selection ends at the beginning of a line, that line is not toggled.
10608 editor.change_selections(None, cx, |s| {
10609 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10610 });
10611 editor.toggle_comments(&ToggleComments, cx);
10612 assert_eq!(
10613 editor.text(cx),
10614 "
10615 fn a() {
10616 // b();
10617 c();
10618 // d();
10619 }
10620 "
10621 .unindent()
10622 );
10623 });
10624 }
10625
10626 #[gpui::test]
10627 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10628 cx.set_global(Settings::test(cx));
10629 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10630 let multibuffer = cx.add_model(|cx| {
10631 let mut multibuffer = MultiBuffer::new(0);
10632 multibuffer.push_excerpts(
10633 buffer.clone(),
10634 [
10635 ExcerptRange {
10636 context: Point::new(0, 0)..Point::new(0, 4),
10637 primary: None,
10638 },
10639 ExcerptRange {
10640 context: Point::new(1, 0)..Point::new(1, 4),
10641 primary: None,
10642 },
10643 ],
10644 cx,
10645 );
10646 multibuffer
10647 });
10648
10649 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10650
10651 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10652 view.update(cx, |view, cx| {
10653 assert_eq!(view.text(cx), "aaaa\nbbbb");
10654 view.change_selections(None, cx, |s| {
10655 s.select_ranges([
10656 Point::new(0, 0)..Point::new(0, 0),
10657 Point::new(1, 0)..Point::new(1, 0),
10658 ])
10659 });
10660
10661 view.handle_input("X", cx);
10662 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10663 assert_eq!(
10664 view.selections.ranges(cx),
10665 [
10666 Point::new(0, 1)..Point::new(0, 1),
10667 Point::new(1, 1)..Point::new(1, 1),
10668 ]
10669 )
10670 });
10671 }
10672
10673 #[gpui::test]
10674 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10675 cx.set_global(Settings::test(cx));
10676 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10677 [aaaa
10678 (bbbb]
10679 cccc)"});
10680 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10681 context,
10682 primary: None,
10683 });
10684 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10685 let multibuffer = cx.add_model(|cx| {
10686 let mut multibuffer = MultiBuffer::new(0);
10687 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10688 multibuffer
10689 });
10690
10691 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10692 view.update(cx, |view, cx| {
10693 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10694 aaaa
10695 b|bbb
10696 b|bb|b
10697 cccc"});
10698 assert_eq!(view.text(cx), expected_text);
10699 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10700
10701 view.handle_input("X", cx);
10702
10703 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10704 aaaa
10705 bX|bbXb
10706 bX|bbX|b
10707 cccc"});
10708 assert_eq!(view.text(cx), expected_text);
10709 assert_eq!(view.selections.ranges(cx), expected_selections);
10710
10711 view.newline(&Newline, cx);
10712 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10713 aaaa
10714 bX
10715 |bbX
10716 b
10717 bX
10718 |bbX
10719 |b
10720 cccc"});
10721 assert_eq!(view.text(cx), expected_text);
10722 assert_eq!(view.selections.ranges(cx), expected_selections);
10723 });
10724 }
10725
10726 #[gpui::test]
10727 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10728 cx.set_global(Settings::test(cx));
10729 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10730 let mut excerpt1_id = None;
10731 let multibuffer = cx.add_model(|cx| {
10732 let mut multibuffer = MultiBuffer::new(0);
10733 excerpt1_id = multibuffer
10734 .push_excerpts(
10735 buffer.clone(),
10736 [
10737 ExcerptRange {
10738 context: Point::new(0, 0)..Point::new(1, 4),
10739 primary: None,
10740 },
10741 ExcerptRange {
10742 context: Point::new(1, 0)..Point::new(2, 4),
10743 primary: None,
10744 },
10745 ],
10746 cx,
10747 )
10748 .into_iter()
10749 .next();
10750 multibuffer
10751 });
10752 assert_eq!(
10753 multibuffer.read(cx).read(cx).text(),
10754 "aaaa\nbbbb\nbbbb\ncccc"
10755 );
10756 let (_, editor) = cx.add_window(Default::default(), |cx| {
10757 let mut editor = build_editor(multibuffer.clone(), cx);
10758 let snapshot = editor.snapshot(cx);
10759 editor.change_selections(None, cx, |s| {
10760 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10761 });
10762 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10763 assert_eq!(
10764 editor.selections.ranges(cx),
10765 [
10766 Point::new(1, 3)..Point::new(1, 3),
10767 Point::new(2, 1)..Point::new(2, 1),
10768 ]
10769 );
10770 editor
10771 });
10772
10773 // Refreshing selections is a no-op when excerpts haven't changed.
10774 editor.update(cx, |editor, cx| {
10775 editor.change_selections(None, cx, |s| {
10776 s.refresh();
10777 });
10778 assert_eq!(
10779 editor.selections.ranges(cx),
10780 [
10781 Point::new(1, 3)..Point::new(1, 3),
10782 Point::new(2, 1)..Point::new(2, 1),
10783 ]
10784 );
10785 });
10786
10787 multibuffer.update(cx, |multibuffer, cx| {
10788 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10789 });
10790 editor.update(cx, |editor, cx| {
10791 // Removing an excerpt causes the first selection to become degenerate.
10792 assert_eq!(
10793 editor.selections.ranges(cx),
10794 [
10795 Point::new(0, 0)..Point::new(0, 0),
10796 Point::new(0, 1)..Point::new(0, 1)
10797 ]
10798 );
10799
10800 // Refreshing selections will relocate the first selection to the original buffer
10801 // location.
10802 editor.change_selections(None, cx, |s| {
10803 s.refresh();
10804 });
10805 assert_eq!(
10806 editor.selections.ranges(cx),
10807 [
10808 Point::new(0, 1)..Point::new(0, 1),
10809 Point::new(0, 3)..Point::new(0, 3)
10810 ]
10811 );
10812 assert!(editor.selections.pending_anchor().is_some());
10813 });
10814 }
10815
10816 #[gpui::test]
10817 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10818 cx.set_global(Settings::test(cx));
10819 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10820 let mut excerpt1_id = None;
10821 let multibuffer = cx.add_model(|cx| {
10822 let mut multibuffer = MultiBuffer::new(0);
10823 excerpt1_id = multibuffer
10824 .push_excerpts(
10825 buffer.clone(),
10826 [
10827 ExcerptRange {
10828 context: Point::new(0, 0)..Point::new(1, 4),
10829 primary: None,
10830 },
10831 ExcerptRange {
10832 context: Point::new(1, 0)..Point::new(2, 4),
10833 primary: None,
10834 },
10835 ],
10836 cx,
10837 )
10838 .into_iter()
10839 .next();
10840 multibuffer
10841 });
10842 assert_eq!(
10843 multibuffer.read(cx).read(cx).text(),
10844 "aaaa\nbbbb\nbbbb\ncccc"
10845 );
10846 let (_, editor) = cx.add_window(Default::default(), |cx| {
10847 let mut editor = build_editor(multibuffer.clone(), cx);
10848 let snapshot = editor.snapshot(cx);
10849 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10850 assert_eq!(
10851 editor.selections.ranges(cx),
10852 [Point::new(1, 3)..Point::new(1, 3)]
10853 );
10854 editor
10855 });
10856
10857 multibuffer.update(cx, |multibuffer, cx| {
10858 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10859 });
10860 editor.update(cx, |editor, cx| {
10861 assert_eq!(
10862 editor.selections.ranges(cx),
10863 [Point::new(0, 0)..Point::new(0, 0)]
10864 );
10865
10866 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10867 editor.change_selections(None, cx, |s| {
10868 s.refresh();
10869 });
10870 assert_eq!(
10871 editor.selections.ranges(cx),
10872 [Point::new(0, 3)..Point::new(0, 3)]
10873 );
10874 assert!(editor.selections.pending_anchor().is_some());
10875 });
10876 }
10877
10878 #[gpui::test]
10879 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10880 cx.update(|cx| cx.set_global(Settings::test(cx)));
10881 let language = Arc::new(
10882 Language::new(
10883 LanguageConfig {
10884 brackets: vec![
10885 BracketPair {
10886 start: "{".to_string(),
10887 end: "}".to_string(),
10888 close: true,
10889 newline: true,
10890 },
10891 BracketPair {
10892 start: "/* ".to_string(),
10893 end: " */".to_string(),
10894 close: true,
10895 newline: true,
10896 },
10897 ],
10898 ..Default::default()
10899 },
10900 Some(tree_sitter_rust::language()),
10901 )
10902 .with_indents_query("")
10903 .unwrap(),
10904 );
10905
10906 let text = concat!(
10907 "{ }\n", // Suppress rustfmt
10908 " x\n", //
10909 " /* */\n", //
10910 "x\n", //
10911 "{{} }\n", //
10912 );
10913
10914 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10915 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10916 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10917 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10918 .await;
10919
10920 view.update(cx, |view, cx| {
10921 view.change_selections(None, cx, |s| {
10922 s.select_display_ranges([
10923 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10924 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10925 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10926 ])
10927 });
10928 view.newline(&Newline, cx);
10929
10930 assert_eq!(
10931 view.buffer().read(cx).read(cx).text(),
10932 concat!(
10933 "{ \n", // Suppress rustfmt
10934 "\n", //
10935 "}\n", //
10936 " x\n", //
10937 " /* \n", //
10938 " \n", //
10939 " */\n", //
10940 "x\n", //
10941 "{{} \n", //
10942 "}\n", //
10943 )
10944 );
10945 });
10946 }
10947
10948 #[gpui::test]
10949 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10950 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10951
10952 cx.set_global(Settings::test(cx));
10953 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10954
10955 editor.update(cx, |editor, cx| {
10956 struct Type1;
10957 struct Type2;
10958
10959 let buffer = buffer.read(cx).snapshot(cx);
10960
10961 let anchor_range = |range: Range<Point>| {
10962 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10963 };
10964
10965 editor.highlight_background::<Type1>(
10966 vec![
10967 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10968 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10969 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10970 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10971 ],
10972 |_| Color::red(),
10973 cx,
10974 );
10975 editor.highlight_background::<Type2>(
10976 vec![
10977 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10978 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10979 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10980 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10981 ],
10982 |_| Color::green(),
10983 cx,
10984 );
10985
10986 let snapshot = editor.snapshot(cx);
10987 let mut highlighted_ranges = editor.background_highlights_in_range(
10988 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10989 &snapshot,
10990 cx.global::<Settings>().theme.as_ref(),
10991 );
10992 // Enforce a consistent ordering based on color without relying on the ordering of the
10993 // highlight's `TypeId` which is non-deterministic.
10994 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10995 assert_eq!(
10996 highlighted_ranges,
10997 &[
10998 (
10999 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
11000 Color::green(),
11001 ),
11002 (
11003 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
11004 Color::green(),
11005 ),
11006 (
11007 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
11008 Color::red(),
11009 ),
11010 (
11011 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11012 Color::red(),
11013 ),
11014 ]
11015 );
11016 assert_eq!(
11017 editor.background_highlights_in_range(
11018 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11019 &snapshot,
11020 cx.global::<Settings>().theme.as_ref(),
11021 ),
11022 &[(
11023 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11024 Color::red(),
11025 )]
11026 );
11027 });
11028 }
11029
11030 #[gpui::test]
11031 fn test_following(cx: &mut gpui::MutableAppContext) {
11032 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11033
11034 cx.set_global(Settings::test(cx));
11035
11036 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11037 let (_, follower) = cx.add_window(
11038 WindowOptions {
11039 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
11040 ..Default::default()
11041 },
11042 |cx| build_editor(buffer.clone(), cx),
11043 );
11044
11045 let pending_update = Rc::new(RefCell::new(None));
11046 follower.update(cx, {
11047 let update = pending_update.clone();
11048 |_, cx| {
11049 cx.subscribe(&leader, move |_, leader, event, cx| {
11050 leader
11051 .read(cx)
11052 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
11053 })
11054 .detach();
11055 }
11056 });
11057
11058 // Update the selections only
11059 leader.update(cx, |leader, cx| {
11060 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
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![1..1]);
11068
11069 // Update the scroll position only
11070 leader.update(cx, |leader, cx| {
11071 leader.set_scroll_position(vec2f(1.5, 3.5), 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!(
11079 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
11080 vec2f(1.5, 3.5)
11081 );
11082
11083 // Update the selections and scroll position
11084 leader.update(cx, |leader, cx| {
11085 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
11086 leader.request_autoscroll(Autoscroll::Newest, cx);
11087 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11088 });
11089 follower.update(cx, |follower, cx| {
11090 let initial_scroll_position = follower.scroll_position(cx);
11091 follower
11092 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11093 .unwrap();
11094 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
11095 assert!(follower.autoscroll_request.is_some());
11096 });
11097 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
11098
11099 // Creating a pending selection that precedes another selection
11100 leader.update(cx, |leader, cx| {
11101 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11102 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
11103 });
11104 follower.update(cx, |follower, cx| {
11105 follower
11106 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11107 .unwrap();
11108 });
11109 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
11110
11111 // Extend the pending selection so that it surrounds another selection
11112 leader.update(cx, |leader, cx| {
11113 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
11114 });
11115 follower.update(cx, |follower, cx| {
11116 follower
11117 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11118 .unwrap();
11119 });
11120 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
11121 }
11122
11123 #[test]
11124 fn test_combine_syntax_and_fuzzy_match_highlights() {
11125 let string = "abcdefghijklmnop";
11126 let syntax_ranges = [
11127 (
11128 0..3,
11129 HighlightStyle {
11130 color: Some(Color::red()),
11131 ..Default::default()
11132 },
11133 ),
11134 (
11135 4..8,
11136 HighlightStyle {
11137 color: Some(Color::green()),
11138 ..Default::default()
11139 },
11140 ),
11141 ];
11142 let match_indices = [4, 6, 7, 8];
11143 assert_eq!(
11144 combine_syntax_and_fuzzy_match_highlights(
11145 &string,
11146 Default::default(),
11147 syntax_ranges.into_iter(),
11148 &match_indices,
11149 ),
11150 &[
11151 (
11152 0..3,
11153 HighlightStyle {
11154 color: Some(Color::red()),
11155 ..Default::default()
11156 },
11157 ),
11158 (
11159 4..5,
11160 HighlightStyle {
11161 color: Some(Color::green()),
11162 weight: Some(fonts::Weight::BOLD),
11163 ..Default::default()
11164 },
11165 ),
11166 (
11167 5..6,
11168 HighlightStyle {
11169 color: Some(Color::green()),
11170 ..Default::default()
11171 },
11172 ),
11173 (
11174 6..8,
11175 HighlightStyle {
11176 color: Some(Color::green()),
11177 weight: Some(fonts::Weight::BOLD),
11178 ..Default::default()
11179 },
11180 ),
11181 (
11182 8..9,
11183 HighlightStyle {
11184 weight: Some(fonts::Weight::BOLD),
11185 ..Default::default()
11186 },
11187 ),
11188 ]
11189 );
11190 }
11191
11192 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11193 let point = DisplayPoint::new(row as u32, column as u32);
11194 point..point
11195 }
11196
11197 fn assert_selection_ranges(
11198 marked_text: &str,
11199 selection_marker_pairs: Vec<(char, char)>,
11200 view: &mut Editor,
11201 cx: &mut ViewContext<Editor>,
11202 ) {
11203 let snapshot = view.snapshot(cx).display_snapshot;
11204 let mut marker_chars = Vec::new();
11205 for (start, end) in selection_marker_pairs.iter() {
11206 marker_chars.push(*start);
11207 marker_chars.push(*end);
11208 }
11209 let (_, markers) = marked_text_by(marked_text, marker_chars);
11210 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
11211 .iter()
11212 .map(|(start, end)| {
11213 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
11214 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
11215 start..end
11216 })
11217 .collect();
11218 assert_eq!(
11219 view.selections.display_ranges(cx),
11220 &asserted_ranges[..],
11221 "Assert selections are {}",
11222 marked_text
11223 );
11224 }
11225}
11226
11227trait RangeExt<T> {
11228 fn sorted(&self) -> Range<T>;
11229 fn to_inclusive(&self) -> RangeInclusive<T>;
11230}
11231
11232impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11233 fn sorted(&self) -> Self {
11234 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11235 }
11236
11237 fn to_inclusive(&self) -> RangeInclusive<T> {
11238 self.start.clone()..=self.end.clone()
11239 }
11240}
11241
11242trait RangeToAnchorExt {
11243 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11244}
11245
11246impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11247 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11248 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11249 }
11250}