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