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