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