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