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