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