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