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