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