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