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