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