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