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