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