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.clone(),
5184 None => return None,
5185 };
5186
5187 Some(self.perform_format(project, cx))
5188 }
5189
5190 fn perform_format(
5191 &mut self,
5192 project: ModelHandle<Project>,
5193 cx: &mut ViewContext<'_, Self>,
5194 ) -> Task<Result<()>> {
5195 let buffer = self.buffer().clone();
5196 let buffers = buffer.read(cx).all_buffers();
5197
5198 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5199 let format = project.update(cx, |project, cx| {
5200 project.format(buffers, true, FormatTrigger::Manual, cx)
5201 });
5202
5203 cx.spawn(|_, mut cx| async move {
5204 let transaction = futures::select_biased! {
5205 _ = timeout => {
5206 log::warn!("timed out waiting for formatting");
5207 None
5208 }
5209 transaction = format.log_err().fuse() => transaction,
5210 };
5211
5212 buffer.update(&mut cx, |buffer, cx| {
5213 if let Some(transaction) = transaction {
5214 if !buffer.is_singleton() {
5215 buffer.push_transaction(&transaction.0);
5216 }
5217 }
5218
5219 cx.notify();
5220 });
5221
5222 Ok(())
5223 })
5224 }
5225
5226 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5227 if let Some(project) = self.project.clone() {
5228 self.buffer.update(cx, |multi_buffer, cx| {
5229 project.update(cx, |project, cx| {
5230 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5231 });
5232 })
5233 }
5234 }
5235
5236 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5237 cx.show_character_palette();
5238 }
5239
5240 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5241 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5242 let buffer = self.buffer.read(cx).snapshot(cx);
5243 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5244 let is_valid = buffer
5245 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5246 .any(|entry| {
5247 entry.diagnostic.is_primary
5248 && !entry.range.is_empty()
5249 && entry.range.start == primary_range_start
5250 && entry.diagnostic.message == active_diagnostics.primary_message
5251 });
5252
5253 if is_valid != active_diagnostics.is_valid {
5254 active_diagnostics.is_valid = is_valid;
5255 let mut new_styles = HashMap::default();
5256 for (block_id, diagnostic) in &active_diagnostics.blocks {
5257 new_styles.insert(
5258 *block_id,
5259 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5260 );
5261 }
5262 self.display_map
5263 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5264 }
5265 }
5266 }
5267
5268 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5269 self.dismiss_diagnostics(cx);
5270 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5271 let buffer = self.buffer.read(cx).snapshot(cx);
5272
5273 let mut primary_range = None;
5274 let mut primary_message = None;
5275 let mut group_end = Point::zero();
5276 let diagnostic_group = buffer
5277 .diagnostic_group::<Point>(group_id)
5278 .map(|entry| {
5279 if entry.range.end > group_end {
5280 group_end = entry.range.end;
5281 }
5282 if entry.diagnostic.is_primary {
5283 primary_range = Some(entry.range.clone());
5284 primary_message = Some(entry.diagnostic.message.clone());
5285 }
5286 entry
5287 })
5288 .collect::<Vec<_>>();
5289 let primary_range = primary_range?;
5290 let primary_message = primary_message?;
5291 let primary_range =
5292 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5293
5294 let blocks = display_map
5295 .insert_blocks(
5296 diagnostic_group.iter().map(|entry| {
5297 let diagnostic = entry.diagnostic.clone();
5298 let message_height = diagnostic.message.lines().count() as u8;
5299 BlockProperties {
5300 style: BlockStyle::Fixed,
5301 position: buffer.anchor_after(entry.range.start),
5302 height: message_height,
5303 render: diagnostic_block_renderer(diagnostic, true),
5304 disposition: BlockDisposition::Below,
5305 }
5306 }),
5307 cx,
5308 )
5309 .into_iter()
5310 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5311 .collect();
5312
5313 Some(ActiveDiagnosticGroup {
5314 primary_range,
5315 primary_message,
5316 blocks,
5317 is_valid: true,
5318 })
5319 });
5320 self.active_diagnostics.is_some()
5321 }
5322
5323 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5324 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5325 self.display_map.update(cx, |display_map, cx| {
5326 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5327 });
5328 cx.notify();
5329 }
5330 }
5331
5332 pub fn set_selections_from_remote(
5333 &mut self,
5334 selections: Vec<Selection<Anchor>>,
5335 cx: &mut ViewContext<Self>,
5336 ) {
5337 let old_cursor_position = self.selections.newest_anchor().head();
5338 self.selections.change_with(cx, |s| {
5339 s.select_anchors(selections);
5340 });
5341 self.selections_did_change(false, &old_cursor_position, cx);
5342 }
5343
5344 fn push_to_selection_history(&mut self) {
5345 self.selection_history.push(SelectionHistoryEntry {
5346 selections: self.selections.disjoint_anchors(),
5347 select_next_state: self.select_next_state.clone(),
5348 add_selections_state: self.add_selections_state.clone(),
5349 });
5350 }
5351
5352 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5353 self.autoscroll_request = Some((autoscroll, true));
5354 cx.notify();
5355 }
5356
5357 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5358 self.autoscroll_request = Some((autoscroll, false));
5359 cx.notify();
5360 }
5361
5362 pub fn transact(
5363 &mut self,
5364 cx: &mut ViewContext<Self>,
5365 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5366 ) -> Option<TransactionId> {
5367 self.start_transaction_at(Instant::now(), cx);
5368 update(self, cx);
5369 self.end_transaction_at(Instant::now(), cx)
5370 }
5371
5372 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5373 self.end_selection(cx);
5374 if let Some(tx_id) = self
5375 .buffer
5376 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5377 {
5378 self.selection_history
5379 .insert_transaction(tx_id, self.selections.disjoint_anchors());
5380 }
5381 }
5382
5383 fn end_transaction_at(
5384 &mut self,
5385 now: Instant,
5386 cx: &mut ViewContext<Self>,
5387 ) -> Option<TransactionId> {
5388 if let Some(tx_id) = self
5389 .buffer
5390 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5391 {
5392 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5393 *end_selections = Some(self.selections.disjoint_anchors());
5394 } else {
5395 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5396 }
5397
5398 cx.emit(Event::Edited);
5399 Some(tx_id)
5400 } else {
5401 None
5402 }
5403 }
5404
5405 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5406 log::info!("Editor::page_up");
5407 }
5408
5409 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5410 log::info!("Editor::page_down");
5411 }
5412
5413 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5414 let mut fold_ranges = Vec::new();
5415
5416 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5417 let selections = self.selections.all::<Point>(cx);
5418 for selection in selections {
5419 let range = selection.display_range(&display_map).sorted();
5420 let buffer_start_row = range.start.to_point(&display_map).row;
5421
5422 for row in (0..=range.end.row()).rev() {
5423 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5424 let fold_range = self.foldable_range_for_line(&display_map, row);
5425 if fold_range.end.row >= buffer_start_row {
5426 fold_ranges.push(fold_range);
5427 if row <= range.start.row() {
5428 break;
5429 }
5430 }
5431 }
5432 }
5433 }
5434
5435 self.fold_ranges(fold_ranges, cx);
5436 }
5437
5438 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5439 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5440 let buffer = &display_map.buffer_snapshot;
5441 let selections = self.selections.all::<Point>(cx);
5442 let ranges = selections
5443 .iter()
5444 .map(|s| {
5445 let range = s.display_range(&display_map).sorted();
5446 let mut start = range.start.to_point(&display_map);
5447 let mut end = range.end.to_point(&display_map);
5448 start.column = 0;
5449 end.column = buffer.line_len(end.row);
5450 start..end
5451 })
5452 .collect::<Vec<_>>();
5453 self.unfold_ranges(ranges, true, cx);
5454 }
5455
5456 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5457 let max_point = display_map.max_point();
5458 if display_row >= max_point.row() {
5459 false
5460 } else {
5461 let (start_indent, is_blank) = display_map.line_indent(display_row);
5462 if is_blank {
5463 false
5464 } else {
5465 for display_row in display_row + 1..=max_point.row() {
5466 let (indent, is_blank) = display_map.line_indent(display_row);
5467 if !is_blank {
5468 return indent > start_indent;
5469 }
5470 }
5471 false
5472 }
5473 }
5474 }
5475
5476 fn foldable_range_for_line(
5477 &self,
5478 display_map: &DisplaySnapshot,
5479 start_row: u32,
5480 ) -> Range<Point> {
5481 let max_point = display_map.max_point();
5482
5483 let (start_indent, _) = display_map.line_indent(start_row);
5484 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5485 let mut end = None;
5486 for row in start_row + 1..=max_point.row() {
5487 let (indent, is_blank) = display_map.line_indent(row);
5488 if !is_blank && indent <= start_indent {
5489 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5490 break;
5491 }
5492 }
5493
5494 let end = end.unwrap_or(max_point);
5495 start.to_point(display_map)..end.to_point(display_map)
5496 }
5497
5498 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5499 let selections = self.selections.all::<Point>(cx);
5500 let ranges = selections.into_iter().map(|s| s.start..s.end);
5501 self.fold_ranges(ranges, cx);
5502 }
5503
5504 pub fn fold_ranges<T: ToOffset>(
5505 &mut self,
5506 ranges: impl IntoIterator<Item = Range<T>>,
5507 cx: &mut ViewContext<Self>,
5508 ) {
5509 let mut ranges = ranges.into_iter().peekable();
5510 if ranges.peek().is_some() {
5511 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5512 self.request_autoscroll(Autoscroll::Fit, cx);
5513 cx.notify();
5514 }
5515 }
5516
5517 pub fn unfold_ranges<T: ToOffset>(
5518 &mut self,
5519 ranges: impl IntoIterator<Item = Range<T>>,
5520 inclusive: bool,
5521 cx: &mut ViewContext<Self>,
5522 ) {
5523 let mut ranges = ranges.into_iter().peekable();
5524 if ranges.peek().is_some() {
5525 self.display_map
5526 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5527 self.request_autoscroll(Autoscroll::Fit, cx);
5528 cx.notify();
5529 }
5530 }
5531
5532 pub fn insert_blocks(
5533 &mut self,
5534 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5535 cx: &mut ViewContext<Self>,
5536 ) -> Vec<BlockId> {
5537 let blocks = self
5538 .display_map
5539 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5540 self.request_autoscroll(Autoscroll::Fit, cx);
5541 blocks
5542 }
5543
5544 pub fn replace_blocks(
5545 &mut self,
5546 blocks: HashMap<BlockId, RenderBlock>,
5547 cx: &mut ViewContext<Self>,
5548 ) {
5549 self.display_map
5550 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5551 self.request_autoscroll(Autoscroll::Fit, cx);
5552 }
5553
5554 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5555 self.display_map.update(cx, |display_map, cx| {
5556 display_map.remove_blocks(block_ids, cx)
5557 });
5558 }
5559
5560 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5561 self.display_map
5562 .update(cx, |map, cx| map.snapshot(cx))
5563 .longest_row()
5564 }
5565
5566 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5567 self.display_map
5568 .update(cx, |map, cx| map.snapshot(cx))
5569 .max_point()
5570 }
5571
5572 pub fn text(&self, cx: &AppContext) -> String {
5573 self.buffer.read(cx).read(cx).text()
5574 }
5575
5576 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5577 self.transact(cx, |this, cx| {
5578 this.buffer
5579 .read(cx)
5580 .as_singleton()
5581 .expect("you can only call set_text on editors for singleton buffers")
5582 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5583 });
5584 }
5585
5586 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5587 self.display_map
5588 .update(cx, |map, cx| map.snapshot(cx))
5589 .text()
5590 }
5591
5592 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5593 let language_name = self
5594 .buffer
5595 .read(cx)
5596 .as_singleton()
5597 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5598 .map(|l| l.name());
5599
5600 let settings = cx.global::<Settings>();
5601 let mode = self
5602 .soft_wrap_mode_override
5603 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5604 match mode {
5605 settings::SoftWrap::None => SoftWrap::None,
5606 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5607 settings::SoftWrap::PreferredLineLength => {
5608 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5609 }
5610 }
5611 }
5612
5613 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5614 self.soft_wrap_mode_override = Some(mode);
5615 cx.notify();
5616 }
5617
5618 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5619 self.display_map
5620 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5621 }
5622
5623 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5624 self.highlighted_rows = rows;
5625 }
5626
5627 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5628 self.highlighted_rows.clone()
5629 }
5630
5631 pub fn highlight_background<T: 'static>(
5632 &mut self,
5633 ranges: Vec<Range<Anchor>>,
5634 color_fetcher: fn(&Theme) -> Color,
5635 cx: &mut ViewContext<Self>,
5636 ) {
5637 self.background_highlights
5638 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5639 cx.notify();
5640 }
5641
5642 #[allow(clippy::type_complexity)]
5643 pub fn clear_background_highlights<T: 'static>(
5644 &mut self,
5645 cx: &mut ViewContext<Self>,
5646 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5647 cx.notify();
5648 self.background_highlights.remove(&TypeId::of::<T>())
5649 }
5650
5651 #[cfg(feature = "test-support")]
5652 pub fn all_background_highlights(
5653 &mut self,
5654 cx: &mut ViewContext<Self>,
5655 ) -> Vec<(Range<DisplayPoint>, Color)> {
5656 let snapshot = self.snapshot(cx);
5657 let buffer = &snapshot.buffer_snapshot;
5658 let start = buffer.anchor_before(0);
5659 let end = buffer.anchor_after(buffer.len());
5660 let theme = cx.global::<Settings>().theme.as_ref();
5661 self.background_highlights_in_range(start..end, &snapshot, theme)
5662 }
5663
5664 fn document_highlights_for_position<'a>(
5665 &'a self,
5666 position: Anchor,
5667 buffer: &'a MultiBufferSnapshot,
5668 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5669 let read_highlights = self
5670 .background_highlights
5671 .get(&TypeId::of::<DocumentHighlightRead>())
5672 .map(|h| &h.1);
5673 let write_highlights = self
5674 .background_highlights
5675 .get(&TypeId::of::<DocumentHighlightWrite>())
5676 .map(|h| &h.1);
5677 let left_position = position.bias_left(buffer);
5678 let right_position = position.bias_right(buffer);
5679 read_highlights
5680 .into_iter()
5681 .chain(write_highlights)
5682 .flat_map(move |ranges| {
5683 let start_ix = match ranges.binary_search_by(|probe| {
5684 let cmp = probe.end.cmp(&left_position, buffer);
5685 if cmp.is_ge() {
5686 Ordering::Greater
5687 } else {
5688 Ordering::Less
5689 }
5690 }) {
5691 Ok(i) | Err(i) => i,
5692 };
5693
5694 let right_position = right_position.clone();
5695 ranges[start_ix..]
5696 .iter()
5697 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
5698 })
5699 }
5700
5701 pub fn background_highlights_in_range(
5702 &self,
5703 search_range: Range<Anchor>,
5704 display_snapshot: &DisplaySnapshot,
5705 theme: &Theme,
5706 ) -> Vec<(Range<DisplayPoint>, Color)> {
5707 let mut results = Vec::new();
5708 let buffer = &display_snapshot.buffer_snapshot;
5709 for (color_fetcher, ranges) in self.background_highlights.values() {
5710 let color = color_fetcher(theme);
5711 let start_ix = match ranges.binary_search_by(|probe| {
5712 let cmp = probe.end.cmp(&search_range.start, buffer);
5713 if cmp.is_gt() {
5714 Ordering::Greater
5715 } else {
5716 Ordering::Less
5717 }
5718 }) {
5719 Ok(i) | Err(i) => i,
5720 };
5721 for range in &ranges[start_ix..] {
5722 if range.start.cmp(&search_range.end, buffer).is_ge() {
5723 break;
5724 }
5725 let start = range
5726 .start
5727 .to_point(buffer)
5728 .to_display_point(display_snapshot);
5729 let end = range
5730 .end
5731 .to_point(buffer)
5732 .to_display_point(display_snapshot);
5733 results.push((start..end, color))
5734 }
5735 }
5736 results
5737 }
5738
5739 pub fn highlight_text<T: 'static>(
5740 &mut self,
5741 ranges: Vec<Range<Anchor>>,
5742 style: HighlightStyle,
5743 cx: &mut ViewContext<Self>,
5744 ) {
5745 self.display_map.update(cx, |map, _| {
5746 map.highlight_text(TypeId::of::<T>(), ranges, style)
5747 });
5748 cx.notify();
5749 }
5750
5751 pub fn text_highlights<'a, T: 'static>(
5752 &'a self,
5753 cx: &'a AppContext,
5754 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
5755 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
5756 }
5757
5758 pub fn clear_text_highlights<T: 'static>(
5759 &mut self,
5760 cx: &mut ViewContext<Self>,
5761 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5762 cx.notify();
5763 self.display_map
5764 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5765 }
5766
5767 fn next_blink_epoch(&mut self) -> usize {
5768 self.blink_epoch += 1;
5769 self.blink_epoch
5770 }
5771
5772 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5773 if !self.focused {
5774 return;
5775 }
5776
5777 self.show_local_cursors = true;
5778 cx.notify();
5779
5780 let epoch = self.next_blink_epoch();
5781 cx.spawn(|this, mut cx| {
5782 let this = this.downgrade();
5783 async move {
5784 Timer::after(CURSOR_BLINK_INTERVAL).await;
5785 if let Some(this) = this.upgrade(&cx) {
5786 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5787 }
5788 }
5789 })
5790 .detach();
5791 }
5792
5793 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5794 if epoch == self.blink_epoch {
5795 self.blinking_paused = false;
5796 self.blink_cursors(epoch, cx);
5797 }
5798 }
5799
5800 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5801 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5802 self.show_local_cursors = !self.show_local_cursors;
5803 cx.notify();
5804
5805 let epoch = self.next_blink_epoch();
5806 cx.spawn(|this, mut cx| {
5807 let this = this.downgrade();
5808 async move {
5809 Timer::after(CURSOR_BLINK_INTERVAL).await;
5810 if let Some(this) = this.upgrade(&cx) {
5811 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5812 }
5813 }
5814 })
5815 .detach();
5816 }
5817 }
5818
5819 pub fn show_local_cursors(&self) -> bool {
5820 self.show_local_cursors && self.focused
5821 }
5822
5823 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5824 cx.notify();
5825 }
5826
5827 fn on_buffer_event(
5828 &mut self,
5829 _: ModelHandle<MultiBuffer>,
5830 event: &language::Event,
5831 cx: &mut ViewContext<Self>,
5832 ) {
5833 match event {
5834 language::Event::Edited => {
5835 self.refresh_active_diagnostics(cx);
5836 self.refresh_code_actions(cx);
5837 cx.emit(Event::BufferEdited);
5838 }
5839 language::Event::Reparsed => cx.emit(Event::Reparsed),
5840 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5841 language::Event::Saved => cx.emit(Event::Saved),
5842 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5843 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5844 language::Event::Closed => cx.emit(Event::Closed),
5845 language::Event::DiagnosticsUpdated => {
5846 self.refresh_active_diagnostics(cx);
5847 }
5848 _ => {}
5849 }
5850 }
5851
5852 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5853 cx.notify();
5854 }
5855
5856 pub fn set_searchable(&mut self, searchable: bool) {
5857 self.searchable = searchable;
5858 }
5859
5860 pub fn searchable(&self) -> bool {
5861 self.searchable
5862 }
5863
5864 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5865 let active_item = workspace.active_item(cx);
5866 let editor_handle = if let Some(editor) = active_item
5867 .as_ref()
5868 .and_then(|item| item.act_as::<Self>(cx))
5869 {
5870 editor
5871 } else {
5872 cx.propagate_action();
5873 return;
5874 };
5875
5876 let editor = editor_handle.read(cx);
5877 let buffer = editor.buffer.read(cx);
5878 if buffer.is_singleton() {
5879 cx.propagate_action();
5880 return;
5881 }
5882
5883 let mut new_selections_by_buffer = HashMap::default();
5884 for selection in editor.selections.all::<usize>(cx) {
5885 for (buffer, mut range) in
5886 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5887 {
5888 if selection.reversed {
5889 mem::swap(&mut range.start, &mut range.end);
5890 }
5891 new_selections_by_buffer
5892 .entry(buffer)
5893 .or_insert(Vec::new())
5894 .push(range)
5895 }
5896 }
5897
5898 editor_handle.update(cx, |editor, cx| {
5899 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5900 });
5901 let pane = workspace.active_pane().clone();
5902 pane.update(cx, |pane, _| pane.disable_history());
5903
5904 // We defer the pane interaction because we ourselves are a workspace item
5905 // and activating a new item causes the pane to call a method on us reentrantly,
5906 // which panics if we're on the stack.
5907 cx.defer(move |workspace, cx| {
5908 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5909 let editor = workspace.open_project_item::<Self>(buffer, cx);
5910 editor.update(cx, |editor, cx| {
5911 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5912 s.select_ranges(ranges);
5913 });
5914 });
5915 }
5916
5917 pane.update(cx, |pane, _| pane.enable_history());
5918 });
5919 }
5920
5921 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5922 let editor = workspace.open_path(action.path.clone(), true, cx);
5923 let position = action.position;
5924 let anchor = action.anchor;
5925 cx.spawn_weak(|_, mut cx| async move {
5926 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5927 editor.update(&mut cx, |editor, cx| {
5928 let buffer = editor.buffer().read(cx).as_singleton()?;
5929 let buffer = buffer.read(cx);
5930 let cursor = if buffer.can_resolve(&anchor) {
5931 language::ToPoint::to_point(&anchor, buffer)
5932 } else {
5933 buffer.clip_point(position, Bias::Left)
5934 };
5935
5936 let nav_history = editor.nav_history.take();
5937 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5938 s.select_ranges([cursor..cursor]);
5939 });
5940 editor.nav_history = nav_history;
5941
5942 Some(())
5943 })?;
5944 Some(())
5945 })
5946 .detach()
5947 }
5948
5949 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
5950 let snapshot = self.buffer.read(cx).read(cx);
5951 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
5952 Some(
5953 ranges
5954 .iter()
5955 .map(move |range| {
5956 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
5957 })
5958 .collect(),
5959 )
5960 }
5961
5962 fn selection_replacement_ranges(
5963 &self,
5964 range: Range<OffsetUtf16>,
5965 cx: &AppContext,
5966 ) -> Vec<Range<OffsetUtf16>> {
5967 let selections = self.selections.all::<OffsetUtf16>(cx);
5968 let newest_selection = selections
5969 .iter()
5970 .max_by_key(|selection| selection.id)
5971 .unwrap();
5972 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
5973 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
5974 let snapshot = self.buffer.read(cx).read(cx);
5975 selections
5976 .into_iter()
5977 .map(|mut selection| {
5978 selection.start.0 =
5979 (selection.start.0 as isize).saturating_add(start_delta) as usize;
5980 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
5981 snapshot.clip_offset_utf16(selection.start, Bias::Left)
5982 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
5983 })
5984 .collect()
5985 }
5986}
5987
5988impl EditorSnapshot {
5989 pub fn is_focused(&self) -> bool {
5990 self.is_focused
5991 }
5992
5993 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5994 self.placeholder_text.as_ref()
5995 }
5996
5997 pub fn scroll_position(&self) -> Vector2F {
5998 compute_scroll_position(
5999 &self.display_snapshot,
6000 self.scroll_position,
6001 &self.scroll_top_anchor,
6002 )
6003 }
6004}
6005
6006impl Deref for EditorSnapshot {
6007 type Target = DisplaySnapshot;
6008
6009 fn deref(&self) -> &Self::Target {
6010 &self.display_snapshot
6011 }
6012}
6013
6014fn compute_scroll_position(
6015 snapshot: &DisplaySnapshot,
6016 mut scroll_position: Vector2F,
6017 scroll_top_anchor: &Anchor,
6018) -> Vector2F {
6019 if *scroll_top_anchor != Anchor::min() {
6020 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
6021 scroll_position.set_y(scroll_top + scroll_position.y());
6022 } else {
6023 scroll_position.set_y(0.);
6024 }
6025 scroll_position
6026}
6027
6028#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6029pub enum Event {
6030 BufferEdited,
6031 Edited,
6032 Reparsed,
6033 Blurred,
6034 DirtyChanged,
6035 Saved,
6036 TitleChanged,
6037 SelectionsChanged { local: bool },
6038 ScrollPositionChanged { local: bool },
6039 Closed,
6040 IgnoredInput,
6041}
6042
6043pub struct EditorFocused(pub ViewHandle<Editor>);
6044pub struct EditorBlurred(pub ViewHandle<Editor>);
6045pub struct EditorReleased(pub WeakViewHandle<Editor>);
6046
6047impl Entity for Editor {
6048 type Event = Event;
6049
6050 fn release(&mut self, cx: &mut MutableAppContext) {
6051 cx.emit_global(EditorReleased(self.handle.clone()));
6052 }
6053}
6054
6055impl View for Editor {
6056 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6057 let style = self.style(cx);
6058 let font_changed = self.display_map.update(cx, |map, cx| {
6059 map.set_font(style.text.font_id, style.text.font_size, cx)
6060 });
6061
6062 if font_changed {
6063 let handle = self.handle.clone();
6064 cx.defer(move |cx| {
6065 if let Some(editor) = handle.upgrade(cx) {
6066 editor.update(cx, |editor, cx| {
6067 hide_hover(editor, cx);
6068 hide_link_definition(editor, cx);
6069 })
6070 }
6071 });
6072 }
6073
6074 Stack::new()
6075 .with_child(
6076 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(),
6077 )
6078 .with_child(ChildView::new(&self.mouse_context_menu).boxed())
6079 .boxed()
6080 }
6081
6082 fn ui_name() -> &'static str {
6083 "Editor"
6084 }
6085
6086 fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6087 let focused_event = EditorFocused(cx.handle());
6088 cx.emit_global(focused_event);
6089 if let Some(rename) = self.pending_rename.as_ref() {
6090 cx.focus(&rename.editor);
6091 } else {
6092 self.focused = true;
6093 self.blink_cursors(self.blink_epoch, cx);
6094 self.buffer.update(cx, |buffer, cx| {
6095 buffer.finalize_last_transaction(cx);
6096 if self.leader_replica_id.is_none() {
6097 buffer.set_active_selections(
6098 &self.selections.disjoint_anchors(),
6099 self.selections.line_mode,
6100 cx,
6101 );
6102 }
6103 });
6104 }
6105 }
6106
6107 fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6108 let blurred_event = EditorBlurred(cx.handle());
6109 cx.emit_global(blurred_event);
6110 self.focused = false;
6111 self.buffer
6112 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6113 self.hide_context_menu(cx);
6114 hide_hover(self, cx);
6115 cx.emit(Event::Blurred);
6116 cx.notify();
6117 }
6118
6119 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6120 let mut context = Self::default_keymap_context();
6121 let mode = match self.mode {
6122 EditorMode::SingleLine => "single_line",
6123 EditorMode::AutoHeight { .. } => "auto_height",
6124 EditorMode::Full => "full",
6125 };
6126 context.map.insert("mode".into(), mode.into());
6127 if self.pending_rename.is_some() {
6128 context.set.insert("renaming".into());
6129 }
6130 match self.context_menu.as_ref() {
6131 Some(ContextMenu::Completions(_)) => {
6132 context.set.insert("showing_completions".into());
6133 }
6134 Some(ContextMenu::CodeActions(_)) => {
6135 context.set.insert("showing_code_actions".into());
6136 }
6137 None => {}
6138 }
6139
6140 for layer in self.keymap_context_layers.values() {
6141 context.extend(layer);
6142 }
6143
6144 context
6145 }
6146
6147 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6148 Some(
6149 self.buffer
6150 .read(cx)
6151 .read(cx)
6152 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6153 .collect(),
6154 )
6155 }
6156
6157 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6158 // Prevent the IME menu from appearing when holding down an alphabetic key
6159 // while input is disabled.
6160 if !self.input_enabled {
6161 return None;
6162 }
6163
6164 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6165 Some(range.start.0..range.end.0)
6166 }
6167
6168 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6169 let snapshot = self.buffer.read(cx).read(cx);
6170 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6171 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6172 }
6173
6174 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6175 self.clear_text_highlights::<InputComposition>(cx);
6176 self.ime_transaction.take();
6177 }
6178
6179 fn replace_text_in_range(
6180 &mut self,
6181 range_utf16: Option<Range<usize>>,
6182 text: &str,
6183 cx: &mut ViewContext<Self>,
6184 ) {
6185 if !self.input_enabled {
6186 cx.emit(Event::IgnoredInput);
6187 return;
6188 }
6189
6190 self.transact(cx, |this, cx| {
6191 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6192 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6193 Some(this.selection_replacement_ranges(range_utf16, cx))
6194 } else {
6195 this.marked_text_ranges(cx)
6196 };
6197
6198 if let Some(new_selected_ranges) = new_selected_ranges {
6199 this.change_selections(None, cx, |selections| {
6200 selections.select_ranges(new_selected_ranges)
6201 });
6202 }
6203 this.handle_input(text, cx);
6204 });
6205
6206 if let Some(transaction) = self.ime_transaction {
6207 self.buffer.update(cx, |buffer, cx| {
6208 buffer.group_until_transaction(transaction, cx);
6209 });
6210 }
6211
6212 self.unmark_text(cx);
6213 }
6214
6215 fn replace_and_mark_text_in_range(
6216 &mut self,
6217 range_utf16: Option<Range<usize>>,
6218 text: &str,
6219 new_selected_range_utf16: Option<Range<usize>>,
6220 cx: &mut ViewContext<Self>,
6221 ) {
6222 if !self.input_enabled {
6223 cx.emit(Event::IgnoredInput);
6224 return;
6225 }
6226
6227 let transaction = self.transact(cx, |this, cx| {
6228 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6229 let snapshot = this.buffer.read(cx).read(cx);
6230 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6231 for marked_range in &mut marked_ranges {
6232 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6233 marked_range.start.0 += relative_range_utf16.start;
6234 marked_range.start =
6235 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6236 marked_range.end =
6237 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6238 }
6239 }
6240 Some(marked_ranges)
6241 } else if let Some(range_utf16) = range_utf16 {
6242 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6243 Some(this.selection_replacement_ranges(range_utf16, cx))
6244 } else {
6245 None
6246 };
6247
6248 if let Some(ranges) = ranges_to_replace {
6249 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6250 }
6251
6252 let marked_ranges = {
6253 let snapshot = this.buffer.read(cx).read(cx);
6254 this.selections
6255 .disjoint_anchors()
6256 .iter()
6257 .map(|selection| {
6258 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6259 })
6260 .collect::<Vec<_>>()
6261 };
6262
6263 if text.is_empty() {
6264 this.unmark_text(cx);
6265 } else {
6266 this.highlight_text::<InputComposition>(
6267 marked_ranges.clone(),
6268 this.style(cx).composition_mark,
6269 cx,
6270 );
6271 }
6272
6273 this.handle_input(text, cx);
6274
6275 if let Some(new_selected_range) = new_selected_range_utf16 {
6276 let snapshot = this.buffer.read(cx).read(cx);
6277 let new_selected_ranges = marked_ranges
6278 .into_iter()
6279 .map(|marked_range| {
6280 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6281 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6282 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6283 snapshot.clip_offset_utf16(new_start, Bias::Left)
6284 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6285 })
6286 .collect::<Vec<_>>();
6287
6288 drop(snapshot);
6289 this.change_selections(None, cx, |selections| {
6290 selections.select_ranges(new_selected_ranges)
6291 });
6292 }
6293 });
6294
6295 self.ime_transaction = self.ime_transaction.or(transaction);
6296 if let Some(transaction) = self.ime_transaction {
6297 self.buffer.update(cx, |buffer, cx| {
6298 buffer.group_until_transaction(transaction, cx);
6299 });
6300 }
6301
6302 if self.text_highlights::<InputComposition>(cx).is_none() {
6303 self.ime_transaction.take();
6304 }
6305 }
6306}
6307
6308fn build_style(
6309 settings: &Settings,
6310 get_field_editor_theme: Option<GetFieldEditorTheme>,
6311 override_text_style: Option<&OverrideTextStyle>,
6312 cx: &AppContext,
6313) -> EditorStyle {
6314 let font_cache = cx.font_cache();
6315
6316 let mut theme = settings.theme.editor.clone();
6317 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6318 let field_editor_theme = get_field_editor_theme(&settings.theme);
6319 theme.text_color = field_editor_theme.text.color;
6320 theme.selection = field_editor_theme.selection;
6321 theme.background = field_editor_theme
6322 .container
6323 .background_color
6324 .unwrap_or_default();
6325 EditorStyle {
6326 text: field_editor_theme.text,
6327 placeholder_text: field_editor_theme.placeholder_text,
6328 theme,
6329 }
6330 } else {
6331 let font_family_id = settings.buffer_font_family;
6332 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6333 let font_properties = Default::default();
6334 let font_id = font_cache
6335 .select_font(font_family_id, &font_properties)
6336 .unwrap();
6337 let font_size = settings.buffer_font_size;
6338 EditorStyle {
6339 text: TextStyle {
6340 color: settings.theme.editor.text_color,
6341 font_family_name,
6342 font_family_id,
6343 font_id,
6344 font_size,
6345 font_properties,
6346 underline: Default::default(),
6347 },
6348 placeholder_text: None,
6349 theme,
6350 }
6351 };
6352
6353 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6354 if let Some(highlighted) = style
6355 .text
6356 .clone()
6357 .highlight(highlight_style, font_cache)
6358 .log_err()
6359 {
6360 style.text = highlighted;
6361 }
6362 }
6363
6364 style
6365}
6366
6367trait SelectionExt {
6368 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6369 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6370 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6371 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6372 -> Range<u32>;
6373}
6374
6375impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6376 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6377 let start = self.start.to_point(buffer);
6378 let end = self.end.to_point(buffer);
6379 if self.reversed {
6380 end..start
6381 } else {
6382 start..end
6383 }
6384 }
6385
6386 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6387 let start = self.start.to_offset(buffer);
6388 let end = self.end.to_offset(buffer);
6389 if self.reversed {
6390 end..start
6391 } else {
6392 start..end
6393 }
6394 }
6395
6396 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6397 let start = self
6398 .start
6399 .to_point(&map.buffer_snapshot)
6400 .to_display_point(map);
6401 let end = self
6402 .end
6403 .to_point(&map.buffer_snapshot)
6404 .to_display_point(map);
6405 if self.reversed {
6406 end..start
6407 } else {
6408 start..end
6409 }
6410 }
6411
6412 fn spanned_rows(
6413 &self,
6414 include_end_if_at_line_start: bool,
6415 map: &DisplaySnapshot,
6416 ) -> Range<u32> {
6417 let start = self.start.to_point(&map.buffer_snapshot);
6418 let mut end = self.end.to_point(&map.buffer_snapshot);
6419 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6420 end.row -= 1;
6421 }
6422
6423 let buffer_start = map.prev_line_boundary(start).0;
6424 let buffer_end = map.next_line_boundary(end).0;
6425 buffer_start.row..buffer_end.row + 1
6426 }
6427}
6428
6429impl<T: InvalidationRegion> InvalidationStack<T> {
6430 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6431 where
6432 S: Clone + ToOffset,
6433 {
6434 while let Some(region) = self.last() {
6435 let all_selections_inside_invalidation_ranges =
6436 if selections.len() == region.ranges().len() {
6437 selections
6438 .iter()
6439 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
6440 .all(|(selection, invalidation_range)| {
6441 let head = selection.head().to_offset(buffer);
6442 invalidation_range.start <= head && invalidation_range.end >= head
6443 })
6444 } else {
6445 false
6446 };
6447
6448 if all_selections_inside_invalidation_ranges {
6449 break;
6450 } else {
6451 self.pop();
6452 }
6453 }
6454 }
6455}
6456
6457impl<T> Default for InvalidationStack<T> {
6458 fn default() -> Self {
6459 Self(Default::default())
6460 }
6461}
6462
6463impl<T> Deref for InvalidationStack<T> {
6464 type Target = Vec<T>;
6465
6466 fn deref(&self) -> &Self::Target {
6467 &self.0
6468 }
6469}
6470
6471impl<T> DerefMut for InvalidationStack<T> {
6472 fn deref_mut(&mut self) -> &mut Self::Target {
6473 &mut self.0
6474 }
6475}
6476
6477impl InvalidationRegion for BracketPairState {
6478 fn ranges(&self) -> &[Range<Anchor>] {
6479 &self.ranges
6480 }
6481}
6482
6483impl InvalidationRegion for SnippetState {
6484 fn ranges(&self) -> &[Range<Anchor>] {
6485 &self.ranges[self.active_index]
6486 }
6487}
6488
6489impl Deref for EditorStyle {
6490 type Target = theme::Editor;
6491
6492 fn deref(&self) -> &Self::Target {
6493 &self.theme
6494 }
6495}
6496
6497pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6498 let mut highlighted_lines = Vec::new();
6499 for line in diagnostic.message.lines() {
6500 highlighted_lines.push(highlight_diagnostic_message(line));
6501 }
6502
6503 Arc::new(move |cx: &mut BlockContext| {
6504 let settings = cx.global::<Settings>();
6505 let theme = &settings.theme.editor;
6506 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6507 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6508 Flex::column()
6509 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6510 Label::new(
6511 line.clone(),
6512 style.message.clone().with_font_size(font_size),
6513 )
6514 .with_highlights(highlights.clone())
6515 .contained()
6516 .with_margin_left(cx.anchor_x)
6517 .boxed()
6518 }))
6519 .aligned()
6520 .left()
6521 .boxed()
6522 })
6523}
6524
6525pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6526 let mut message_without_backticks = String::new();
6527 let mut prev_offset = 0;
6528 let mut inside_block = false;
6529 let mut highlights = Vec::new();
6530 for (match_ix, (offset, _)) in message
6531 .match_indices('`')
6532 .chain([(message.len(), "")])
6533 .enumerate()
6534 {
6535 message_without_backticks.push_str(&message[prev_offset..offset]);
6536 if inside_block {
6537 highlights.extend(prev_offset - match_ix..offset - match_ix);
6538 }
6539
6540 inside_block = !inside_block;
6541 prev_offset = offset + 1;
6542 }
6543
6544 (message_without_backticks, highlights)
6545}
6546
6547pub fn diagnostic_style(
6548 severity: DiagnosticSeverity,
6549 valid: bool,
6550 theme: &theme::Editor,
6551) -> DiagnosticStyle {
6552 match (severity, valid) {
6553 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6554 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6555 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6556 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6557 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6558 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6559 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6560 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6561 _ => theme.invalid_hint_diagnostic.clone(),
6562 }
6563}
6564
6565pub fn combine_syntax_and_fuzzy_match_highlights(
6566 text: &str,
6567 default_style: HighlightStyle,
6568 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6569 match_indices: &[usize],
6570) -> Vec<(Range<usize>, HighlightStyle)> {
6571 let mut result = Vec::new();
6572 let mut match_indices = match_indices.iter().copied().peekable();
6573
6574 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6575 {
6576 syntax_highlight.weight = None;
6577
6578 // Add highlights for any fuzzy match characters before the next
6579 // syntax highlight range.
6580 while let Some(&match_index) = match_indices.peek() {
6581 if match_index >= range.start {
6582 break;
6583 }
6584 match_indices.next();
6585 let end_index = char_ix_after(match_index, text);
6586 let mut match_style = default_style;
6587 match_style.weight = Some(fonts::Weight::BOLD);
6588 result.push((match_index..end_index, match_style));
6589 }
6590
6591 if range.start == usize::MAX {
6592 break;
6593 }
6594
6595 // Add highlights for any fuzzy match characters within the
6596 // syntax highlight range.
6597 let mut offset = range.start;
6598 while let Some(&match_index) = match_indices.peek() {
6599 if match_index >= range.end {
6600 break;
6601 }
6602
6603 match_indices.next();
6604 if match_index > offset {
6605 result.push((offset..match_index, syntax_highlight));
6606 }
6607
6608 let mut end_index = char_ix_after(match_index, text);
6609 while let Some(&next_match_index) = match_indices.peek() {
6610 if next_match_index == end_index && next_match_index < range.end {
6611 end_index = char_ix_after(next_match_index, text);
6612 match_indices.next();
6613 } else {
6614 break;
6615 }
6616 }
6617
6618 let mut match_style = syntax_highlight;
6619 match_style.weight = Some(fonts::Weight::BOLD);
6620 result.push((match_index..end_index, match_style));
6621 offset = end_index;
6622 }
6623
6624 if offset < range.end {
6625 result.push((offset..range.end, syntax_highlight));
6626 }
6627 }
6628
6629 fn char_ix_after(ix: usize, text: &str) -> usize {
6630 ix + text[ix..].chars().next().unwrap().len_utf8()
6631 }
6632
6633 result
6634}
6635
6636pub fn styled_runs_for_code_label<'a>(
6637 label: &'a CodeLabel,
6638 syntax_theme: &'a theme::SyntaxTheme,
6639) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6640 let fade_out = HighlightStyle {
6641 fade_out: Some(0.35),
6642 ..Default::default()
6643 };
6644
6645 let mut prev_end = label.filter_range.end;
6646 label
6647 .runs
6648 .iter()
6649 .enumerate()
6650 .flat_map(move |(ix, (range, highlight_id))| {
6651 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6652 style
6653 } else {
6654 return Default::default();
6655 };
6656 let mut muted_style = style;
6657 muted_style.highlight(fade_out);
6658
6659 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6660 if range.start >= label.filter_range.end {
6661 if range.start > prev_end {
6662 runs.push((prev_end..range.start, fade_out));
6663 }
6664 runs.push((range.clone(), muted_style));
6665 } else if range.end <= label.filter_range.end {
6666 runs.push((range.clone(), style));
6667 } else {
6668 runs.push((range.start..label.filter_range.end, style));
6669 runs.push((label.filter_range.end..range.end, muted_style));
6670 }
6671 prev_end = cmp::max(prev_end, range.end);
6672
6673 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6674 runs.push((prev_end..label.text.len(), fade_out));
6675 }
6676
6677 runs
6678 })
6679}
6680
6681#[cfg(test)]
6682mod tests {
6683 use crate::test::{
6684 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6685 EditorTestContext,
6686 };
6687
6688 use super::*;
6689 use futures::StreamExt;
6690 use gpui::{
6691 geometry::rect::RectF,
6692 platform::{WindowBounds, WindowOptions},
6693 };
6694 use indoc::indoc;
6695 use language::{FakeLspAdapter, LanguageConfig};
6696 use project::FakeFs;
6697 use settings::EditorSettings;
6698 use std::{cell::RefCell, rc::Rc, time::Instant};
6699 use text::Point;
6700 use unindent::Unindent;
6701 use util::{
6702 assert_set_eq,
6703 test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
6704 };
6705 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6706
6707 #[gpui::test]
6708 fn test_edit_events(cx: &mut MutableAppContext) {
6709 cx.set_global(Settings::test(cx));
6710 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6711
6712 let events = Rc::new(RefCell::new(Vec::new()));
6713 let (_, editor1) = cx.add_window(Default::default(), {
6714 let events = events.clone();
6715 |cx| {
6716 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6717 if matches!(
6718 event,
6719 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6720 ) {
6721 events.borrow_mut().push(("editor1", *event));
6722 }
6723 })
6724 .detach();
6725 Editor::for_buffer(buffer.clone(), None, cx)
6726 }
6727 });
6728 let (_, editor2) = cx.add_window(Default::default(), {
6729 let events = events.clone();
6730 |cx| {
6731 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6732 if matches!(
6733 event,
6734 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6735 ) {
6736 events.borrow_mut().push(("editor2", *event));
6737 }
6738 })
6739 .detach();
6740 Editor::for_buffer(buffer.clone(), None, cx)
6741 }
6742 });
6743 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6744
6745 // Mutating editor 1 will emit an `Edited` event only for that editor.
6746 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6747 assert_eq!(
6748 mem::take(&mut *events.borrow_mut()),
6749 [
6750 ("editor1", Event::Edited),
6751 ("editor1", Event::BufferEdited),
6752 ("editor2", Event::BufferEdited),
6753 ("editor1", Event::DirtyChanged),
6754 ("editor2", Event::DirtyChanged)
6755 ]
6756 );
6757
6758 // Mutating editor 2 will emit an `Edited` event only for that editor.
6759 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6760 assert_eq!(
6761 mem::take(&mut *events.borrow_mut()),
6762 [
6763 ("editor2", Event::Edited),
6764 ("editor1", Event::BufferEdited),
6765 ("editor2", Event::BufferEdited),
6766 ]
6767 );
6768
6769 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6770 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6771 assert_eq!(
6772 mem::take(&mut *events.borrow_mut()),
6773 [
6774 ("editor1", Event::Edited),
6775 ("editor1", Event::BufferEdited),
6776 ("editor2", Event::BufferEdited),
6777 ("editor1", Event::DirtyChanged),
6778 ("editor2", Event::DirtyChanged),
6779 ]
6780 );
6781
6782 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6783 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6784 assert_eq!(
6785 mem::take(&mut *events.borrow_mut()),
6786 [
6787 ("editor1", Event::Edited),
6788 ("editor1", Event::BufferEdited),
6789 ("editor2", Event::BufferEdited),
6790 ("editor1", Event::DirtyChanged),
6791 ("editor2", Event::DirtyChanged),
6792 ]
6793 );
6794
6795 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6796 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6797 assert_eq!(
6798 mem::take(&mut *events.borrow_mut()),
6799 [
6800 ("editor2", Event::Edited),
6801 ("editor1", Event::BufferEdited),
6802 ("editor2", Event::BufferEdited),
6803 ("editor1", Event::DirtyChanged),
6804 ("editor2", Event::DirtyChanged),
6805 ]
6806 );
6807
6808 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6809 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6810 assert_eq!(
6811 mem::take(&mut *events.borrow_mut()),
6812 [
6813 ("editor2", Event::Edited),
6814 ("editor1", Event::BufferEdited),
6815 ("editor2", Event::BufferEdited),
6816 ("editor1", Event::DirtyChanged),
6817 ("editor2", Event::DirtyChanged),
6818 ]
6819 );
6820
6821 // No event is emitted when the mutation is a no-op.
6822 editor2.update(cx, |editor, cx| {
6823 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6824
6825 editor.backspace(&Backspace, cx);
6826 });
6827 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6828 }
6829
6830 #[gpui::test]
6831 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6832 cx.set_global(Settings::test(cx));
6833 let mut now = Instant::now();
6834 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6835 let group_interval = buffer.read(cx).transaction_group_interval();
6836 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6837 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6838
6839 editor.update(cx, |editor, cx| {
6840 editor.start_transaction_at(now, cx);
6841 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6842
6843 editor.insert("cd", cx);
6844 editor.end_transaction_at(now, cx);
6845 assert_eq!(editor.text(cx), "12cd56");
6846 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6847
6848 editor.start_transaction_at(now, cx);
6849 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6850 editor.insert("e", cx);
6851 editor.end_transaction_at(now, cx);
6852 assert_eq!(editor.text(cx), "12cde6");
6853 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6854
6855 now += group_interval + Duration::from_millis(1);
6856 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6857
6858 // Simulate an edit in another editor
6859 buffer.update(cx, |buffer, cx| {
6860 buffer.start_transaction_at(now, cx);
6861 buffer.edit([(0..1, "a")], None, cx);
6862 buffer.edit([(1..1, "b")], None, cx);
6863 buffer.end_transaction_at(now, cx);
6864 });
6865
6866 assert_eq!(editor.text(cx), "ab2cde6");
6867 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6868
6869 // Last transaction happened past the group interval in a different editor.
6870 // Undo it individually and don't restore selections.
6871 editor.undo(&Undo, cx);
6872 assert_eq!(editor.text(cx), "12cde6");
6873 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6874
6875 // First two transactions happened within the group interval in this editor.
6876 // Undo them together and restore selections.
6877 editor.undo(&Undo, cx);
6878 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6879 assert_eq!(editor.text(cx), "123456");
6880 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6881
6882 // Redo the first two transactions together.
6883 editor.redo(&Redo, cx);
6884 assert_eq!(editor.text(cx), "12cde6");
6885 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6886
6887 // Redo the last transaction on its own.
6888 editor.redo(&Redo, cx);
6889 assert_eq!(editor.text(cx), "ab2cde6");
6890 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6891
6892 // Test empty transactions.
6893 editor.start_transaction_at(now, cx);
6894 editor.end_transaction_at(now, cx);
6895 editor.undo(&Undo, cx);
6896 assert_eq!(editor.text(cx), "12cde6");
6897 });
6898 }
6899
6900 #[gpui::test]
6901 fn test_ime_composition(cx: &mut MutableAppContext) {
6902 cx.set_global(Settings::test(cx));
6903 let buffer = cx.add_model(|cx| {
6904 let mut buffer = language::Buffer::new(0, "abcde", cx);
6905 // Ensure automatic grouping doesn't occur.
6906 buffer.set_group_interval(Duration::ZERO);
6907 buffer
6908 });
6909
6910 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6911 cx.add_window(Default::default(), |cx| {
6912 let mut editor = build_editor(buffer.clone(), cx);
6913
6914 // Start a new IME composition.
6915 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6916 editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
6917 editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
6918 assert_eq!(editor.text(cx), "äbcde");
6919 assert_eq!(
6920 editor.marked_text_ranges(cx),
6921 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6922 );
6923
6924 // Finalize IME composition.
6925 editor.replace_text_in_range(None, "ā", cx);
6926 assert_eq!(editor.text(cx), "ābcde");
6927 assert_eq!(editor.marked_text_ranges(cx), None);
6928
6929 // IME composition edits are grouped and are undone/redone at once.
6930 editor.undo(&Default::default(), cx);
6931 assert_eq!(editor.text(cx), "abcde");
6932 assert_eq!(editor.marked_text_ranges(cx), None);
6933 editor.redo(&Default::default(), cx);
6934 assert_eq!(editor.text(cx), "ābcde");
6935 assert_eq!(editor.marked_text_ranges(cx), None);
6936
6937 // Start a new IME composition.
6938 editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
6939 assert_eq!(
6940 editor.marked_text_ranges(cx),
6941 Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
6942 );
6943
6944 // Undoing during an IME composition cancels it.
6945 editor.undo(&Default::default(), cx);
6946 assert_eq!(editor.text(cx), "ābcde");
6947 assert_eq!(editor.marked_text_ranges(cx), None);
6948
6949 // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
6950 editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
6951 assert_eq!(editor.text(cx), "ābcdè");
6952 assert_eq!(
6953 editor.marked_text_ranges(cx),
6954 Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
6955 );
6956
6957 // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
6958 editor.replace_text_in_range(Some(4..999), "ę", cx);
6959 assert_eq!(editor.text(cx), "ābcdę");
6960 assert_eq!(editor.marked_text_ranges(cx), None);
6961
6962 // Start a new IME composition with multiple cursors.
6963 editor.change_selections(None, cx, |s| {
6964 s.select_ranges([
6965 OffsetUtf16(1)..OffsetUtf16(1),
6966 OffsetUtf16(3)..OffsetUtf16(3),
6967 OffsetUtf16(5)..OffsetUtf16(5),
6968 ])
6969 });
6970 editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
6971 assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
6972 assert_eq!(
6973 editor.marked_text_ranges(cx),
6974 Some(vec![
6975 OffsetUtf16(0)..OffsetUtf16(3),
6976 OffsetUtf16(4)..OffsetUtf16(7),
6977 OffsetUtf16(8)..OffsetUtf16(11)
6978 ])
6979 );
6980
6981 // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
6982 editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
6983 assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
6984 assert_eq!(
6985 editor.marked_text_ranges(cx),
6986 Some(vec![
6987 OffsetUtf16(1)..OffsetUtf16(2),
6988 OffsetUtf16(5)..OffsetUtf16(6),
6989 OffsetUtf16(9)..OffsetUtf16(10)
6990 ])
6991 );
6992
6993 // Finalize IME composition with multiple cursors.
6994 editor.replace_text_in_range(Some(9..10), "2", cx);
6995 assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
6996 assert_eq!(editor.marked_text_ranges(cx), None);
6997
6998 editor
6999 });
7000 }
7001
7002 #[gpui::test]
7003 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
7004 cx.set_global(Settings::test(cx));
7005
7006 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
7007 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7008 editor.update(cx, |view, cx| {
7009 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7010 });
7011 assert_eq!(
7012 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7013 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7014 );
7015
7016 editor.update(cx, |view, cx| {
7017 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7018 });
7019
7020 assert_eq!(
7021 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7022 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7023 );
7024
7025 editor.update(cx, |view, cx| {
7026 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7027 });
7028
7029 assert_eq!(
7030 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7031 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7032 );
7033
7034 editor.update(cx, |view, cx| {
7035 view.end_selection(cx);
7036 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7037 });
7038
7039 assert_eq!(
7040 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7041 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
7042 );
7043
7044 editor.update(cx, |view, cx| {
7045 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
7046 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
7047 });
7048
7049 assert_eq!(
7050 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7051 [
7052 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
7053 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
7054 ]
7055 );
7056
7057 editor.update(cx, |view, cx| {
7058 view.end_selection(cx);
7059 });
7060
7061 assert_eq!(
7062 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
7063 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
7064 );
7065 }
7066
7067 #[gpui::test]
7068 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
7069 cx.set_global(Settings::test(cx));
7070 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7071 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7072
7073 view.update(cx, |view, cx| {
7074 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
7075 assert_eq!(
7076 view.selections.display_ranges(cx),
7077 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
7078 );
7079 });
7080
7081 view.update(cx, |view, cx| {
7082 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
7083 assert_eq!(
7084 view.selections.display_ranges(cx),
7085 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7086 );
7087 });
7088
7089 view.update(cx, |view, cx| {
7090 view.cancel(&Cancel, cx);
7091 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7092 assert_eq!(
7093 view.selections.display_ranges(cx),
7094 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
7095 );
7096 });
7097 }
7098
7099 #[gpui::test]
7100 fn test_clone(cx: &mut gpui::MutableAppContext) {
7101 let (text, selection_ranges) = marked_text_ranges(
7102 indoc! {"
7103 one
7104 two
7105 threeˇ
7106 four
7107 fiveˇ
7108 "},
7109 true,
7110 );
7111 cx.set_global(Settings::test(cx));
7112 let buffer = MultiBuffer::build_simple(&text, cx);
7113
7114 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7115
7116 editor.update(cx, |editor, cx| {
7117 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
7118 editor.fold_ranges(
7119 [
7120 Point::new(1, 0)..Point::new(2, 0),
7121 Point::new(3, 0)..Point::new(4, 0),
7122 ],
7123 cx,
7124 );
7125 });
7126
7127 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
7128 cx.add_window(Default::default(), |cx| editor.clone(cx))
7129 });
7130
7131 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
7132 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
7133
7134 assert_eq!(
7135 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
7136 editor.update(cx, |e, cx| e.display_text(cx))
7137 );
7138 assert_eq!(
7139 cloned_snapshot
7140 .folds_in_range(0..text.len())
7141 .collect::<Vec<_>>(),
7142 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
7143 );
7144 assert_set_eq!(
7145 cloned_editor.read(cx).selections.ranges::<Point>(cx),
7146 editor.read(cx).selections.ranges(cx)
7147 );
7148 assert_set_eq!(
7149 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
7150 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
7151 );
7152 }
7153
7154 #[gpui::test]
7155 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
7156 cx.set_global(Settings::test(cx));
7157 use workspace::Item;
7158 let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx));
7159 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
7160
7161 cx.add_view(&pane, |cx| {
7162 let mut editor = build_editor(buffer.clone(), cx);
7163 let handle = cx.handle();
7164 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
7165
7166 fn pop_history(
7167 editor: &mut Editor,
7168 cx: &mut MutableAppContext,
7169 ) -> Option<NavigationEntry> {
7170 editor.nav_history.as_mut().unwrap().pop_backward(cx)
7171 }
7172
7173 // Move the cursor a small distance.
7174 // Nothing is added to the navigation history.
7175 editor.change_selections(None, cx, |s| {
7176 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
7177 });
7178 editor.change_selections(None, cx, |s| {
7179 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
7180 });
7181 assert!(pop_history(&mut editor, cx).is_none());
7182
7183 // Move the cursor a large distance.
7184 // The history can jump back to the previous position.
7185 editor.change_selections(None, cx, |s| {
7186 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
7187 });
7188 let nav_entry = pop_history(&mut editor, cx).unwrap();
7189 editor.navigate(nav_entry.data.unwrap(), cx);
7190 assert_eq!(nav_entry.item.id(), cx.view_id());
7191 assert_eq!(
7192 editor.selections.display_ranges(cx),
7193 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
7194 );
7195 assert!(pop_history(&mut editor, cx).is_none());
7196
7197 // Move the cursor a small distance via the mouse.
7198 // Nothing is added to the navigation history.
7199 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
7200 editor.end_selection(cx);
7201 assert_eq!(
7202 editor.selections.display_ranges(cx),
7203 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7204 );
7205 assert!(pop_history(&mut editor, cx).is_none());
7206
7207 // Move the cursor a large distance via the mouse.
7208 // The history can jump back to the previous position.
7209 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
7210 editor.end_selection(cx);
7211 assert_eq!(
7212 editor.selections.display_ranges(cx),
7213 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
7214 );
7215 let nav_entry = pop_history(&mut editor, cx).unwrap();
7216 editor.navigate(nav_entry.data.unwrap(), cx);
7217 assert_eq!(nav_entry.item.id(), cx.view_id());
7218 assert_eq!(
7219 editor.selections.display_ranges(cx),
7220 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
7221 );
7222 assert!(pop_history(&mut editor, cx).is_none());
7223
7224 // Set scroll position to check later
7225 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
7226 let original_scroll_position = editor.scroll_position;
7227 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
7228
7229 // Jump to the end of the document and adjust scroll
7230 editor.move_to_end(&MoveToEnd, cx);
7231 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
7232 assert_ne!(editor.scroll_position, original_scroll_position);
7233 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
7234
7235 let nav_entry = pop_history(&mut editor, cx).unwrap();
7236 editor.navigate(nav_entry.data.unwrap(), cx);
7237 assert_eq!(editor.scroll_position, original_scroll_position);
7238 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
7239
7240 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
7241 let mut invalid_anchor = editor.scroll_top_anchor.clone();
7242 invalid_anchor.text_anchor.buffer_id = Some(999);
7243 let invalid_point = Point::new(9999, 0);
7244 editor.navigate(
7245 Box::new(NavigationData {
7246 cursor_anchor: invalid_anchor.clone(),
7247 cursor_position: invalid_point,
7248 scroll_top_anchor: invalid_anchor,
7249 scroll_top_row: invalid_point.row,
7250 scroll_position: Default::default(),
7251 }),
7252 cx,
7253 );
7254 assert_eq!(
7255 editor.selections.display_ranges(cx),
7256 &[editor.max_point(cx)..editor.max_point(cx)]
7257 );
7258 assert_eq!(
7259 editor.scroll_position(cx),
7260 vec2f(0., editor.max_point(cx).row() as f32)
7261 );
7262
7263 editor
7264 });
7265 }
7266
7267 #[gpui::test]
7268 fn test_cancel(cx: &mut gpui::MutableAppContext) {
7269 cx.set_global(Settings::test(cx));
7270 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
7271 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7272
7273 view.update(cx, |view, cx| {
7274 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
7275 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
7276 view.end_selection(cx);
7277
7278 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
7279 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
7280 view.end_selection(cx);
7281 assert_eq!(
7282 view.selections.display_ranges(cx),
7283 [
7284 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7285 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
7286 ]
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(3, 4)..DisplayPoint::new(1, 1)]
7295 );
7296 });
7297
7298 view.update(cx, |view, cx| {
7299 view.cancel(&Cancel, cx);
7300 assert_eq!(
7301 view.selections.display_ranges(cx),
7302 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
7303 );
7304 });
7305 }
7306
7307 #[gpui::test]
7308 fn test_fold(cx: &mut gpui::MutableAppContext) {
7309 cx.set_global(Settings::test(cx));
7310 let buffer = MultiBuffer::build_simple(
7311 &"
7312 impl Foo {
7313 // Hello!
7314
7315 fn a() {
7316 1
7317 }
7318
7319 fn b() {
7320 2
7321 }
7322
7323 fn c() {
7324 3
7325 }
7326 }
7327 "
7328 .unindent(),
7329 cx,
7330 );
7331 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7332
7333 view.update(cx, |view, cx| {
7334 view.change_selections(None, cx, |s| {
7335 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
7336 });
7337 view.fold(&Fold, cx);
7338 assert_eq!(
7339 view.display_text(cx),
7340 "
7341 impl Foo {
7342 // Hello!
7343
7344 fn a() {
7345 1
7346 }
7347
7348 fn b() {…
7349 }
7350
7351 fn c() {…
7352 }
7353 }
7354 "
7355 .unindent(),
7356 );
7357
7358 view.fold(&Fold, cx);
7359 assert_eq!(
7360 view.display_text(cx),
7361 "
7362 impl Foo {…
7363 }
7364 "
7365 .unindent(),
7366 );
7367
7368 view.unfold_lines(&UnfoldLines, cx);
7369 assert_eq!(
7370 view.display_text(cx),
7371 "
7372 impl Foo {
7373 // Hello!
7374
7375 fn a() {
7376 1
7377 }
7378
7379 fn b() {…
7380 }
7381
7382 fn c() {…
7383 }
7384 }
7385 "
7386 .unindent(),
7387 );
7388
7389 view.unfold_lines(&UnfoldLines, cx);
7390 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7391 });
7392 }
7393
7394 #[gpui::test]
7395 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7396 cx.set_global(Settings::test(cx));
7397 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7398 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7399
7400 buffer.update(cx, |buffer, cx| {
7401 buffer.edit(
7402 vec![
7403 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7404 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7405 ],
7406 None,
7407 cx,
7408 );
7409 });
7410
7411 view.update(cx, |view, cx| {
7412 assert_eq!(
7413 view.selections.display_ranges(cx),
7414 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7415 );
7416
7417 view.move_down(&MoveDown, cx);
7418 assert_eq!(
7419 view.selections.display_ranges(cx),
7420 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7421 );
7422
7423 view.move_right(&MoveRight, cx);
7424 assert_eq!(
7425 view.selections.display_ranges(cx),
7426 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7427 );
7428
7429 view.move_left(&MoveLeft, cx);
7430 assert_eq!(
7431 view.selections.display_ranges(cx),
7432 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7433 );
7434
7435 view.move_up(&MoveUp, cx);
7436 assert_eq!(
7437 view.selections.display_ranges(cx),
7438 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7439 );
7440
7441 view.move_to_end(&MoveToEnd, cx);
7442 assert_eq!(
7443 view.selections.display_ranges(cx),
7444 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7445 );
7446
7447 view.move_to_beginning(&MoveToBeginning, cx);
7448 assert_eq!(
7449 view.selections.display_ranges(cx),
7450 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7451 );
7452
7453 view.change_selections(None, cx, |s| {
7454 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
7455 });
7456 view.select_to_beginning(&SelectToBeginning, cx);
7457 assert_eq!(
7458 view.selections.display_ranges(cx),
7459 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7460 );
7461
7462 view.select_to_end(&SelectToEnd, cx);
7463 assert_eq!(
7464 view.selections.display_ranges(cx),
7465 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7466 );
7467 });
7468 }
7469
7470 #[gpui::test]
7471 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7472 cx.set_global(Settings::test(cx));
7473 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7474 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7475
7476 assert_eq!('ⓐ'.len_utf8(), 3);
7477 assert_eq!('α'.len_utf8(), 2);
7478
7479 view.update(cx, |view, cx| {
7480 view.fold_ranges(
7481 vec![
7482 Point::new(0, 6)..Point::new(0, 12),
7483 Point::new(1, 2)..Point::new(1, 4),
7484 Point::new(2, 4)..Point::new(2, 8),
7485 ],
7486 cx,
7487 );
7488 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7489
7490 view.move_right(&MoveRight, cx);
7491 assert_eq!(
7492 view.selections.display_ranges(cx),
7493 &[empty_range(0, "ⓐ".len())]
7494 );
7495 view.move_right(&MoveRight, cx);
7496 assert_eq!(
7497 view.selections.display_ranges(cx),
7498 &[empty_range(0, "ⓐⓑ".len())]
7499 );
7500 view.move_right(&MoveRight, cx);
7501 assert_eq!(
7502 view.selections.display_ranges(cx),
7503 &[empty_range(0, "ⓐⓑ…".len())]
7504 );
7505
7506 view.move_down(&MoveDown, cx);
7507 assert_eq!(
7508 view.selections.display_ranges(cx),
7509 &[empty_range(1, "ab…".len())]
7510 );
7511 view.move_left(&MoveLeft, cx);
7512 assert_eq!(
7513 view.selections.display_ranges(cx),
7514 &[empty_range(1, "ab".len())]
7515 );
7516 view.move_left(&MoveLeft, cx);
7517 assert_eq!(
7518 view.selections.display_ranges(cx),
7519 &[empty_range(1, "a".len())]
7520 );
7521
7522 view.move_down(&MoveDown, cx);
7523 assert_eq!(
7524 view.selections.display_ranges(cx),
7525 &[empty_range(2, "α".len())]
7526 );
7527 view.move_right(&MoveRight, cx);
7528 assert_eq!(
7529 view.selections.display_ranges(cx),
7530 &[empty_range(2, "αβ".len())]
7531 );
7532 view.move_right(&MoveRight, cx);
7533 assert_eq!(
7534 view.selections.display_ranges(cx),
7535 &[empty_range(2, "αβ…".len())]
7536 );
7537 view.move_right(&MoveRight, cx);
7538 assert_eq!(
7539 view.selections.display_ranges(cx),
7540 &[empty_range(2, "αβ…ε".len())]
7541 );
7542
7543 view.move_up(&MoveUp, cx);
7544 assert_eq!(
7545 view.selections.display_ranges(cx),
7546 &[empty_range(1, "ab…e".len())]
7547 );
7548 view.move_up(&MoveUp, cx);
7549 assert_eq!(
7550 view.selections.display_ranges(cx),
7551 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7552 );
7553 view.move_left(&MoveLeft, cx);
7554 assert_eq!(
7555 view.selections.display_ranges(cx),
7556 &[empty_range(0, "ⓐⓑ…".len())]
7557 );
7558 view.move_left(&MoveLeft, cx);
7559 assert_eq!(
7560 view.selections.display_ranges(cx),
7561 &[empty_range(0, "ⓐⓑ".len())]
7562 );
7563 view.move_left(&MoveLeft, cx);
7564 assert_eq!(
7565 view.selections.display_ranges(cx),
7566 &[empty_range(0, "ⓐ".len())]
7567 );
7568 });
7569 }
7570
7571 #[gpui::test]
7572 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7573 cx.set_global(Settings::test(cx));
7574 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7575 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7576 view.update(cx, |view, cx| {
7577 view.change_selections(None, cx, |s| {
7578 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7579 });
7580 view.move_down(&MoveDown, cx);
7581 assert_eq!(
7582 view.selections.display_ranges(cx),
7583 &[empty_range(1, "abcd".len())]
7584 );
7585
7586 view.move_down(&MoveDown, cx);
7587 assert_eq!(
7588 view.selections.display_ranges(cx),
7589 &[empty_range(2, "αβγ".len())]
7590 );
7591
7592 view.move_down(&MoveDown, cx);
7593 assert_eq!(
7594 view.selections.display_ranges(cx),
7595 &[empty_range(3, "abcd".len())]
7596 );
7597
7598 view.move_down(&MoveDown, cx);
7599 assert_eq!(
7600 view.selections.display_ranges(cx),
7601 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7602 );
7603
7604 view.move_up(&MoveUp, cx);
7605 assert_eq!(
7606 view.selections.display_ranges(cx),
7607 &[empty_range(3, "abcd".len())]
7608 );
7609
7610 view.move_up(&MoveUp, cx);
7611 assert_eq!(
7612 view.selections.display_ranges(cx),
7613 &[empty_range(2, "αβγ".len())]
7614 );
7615 });
7616 }
7617
7618 #[gpui::test]
7619 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7620 cx.set_global(Settings::test(cx));
7621 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7622 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7623 view.update(cx, |view, cx| {
7624 view.change_selections(None, cx, |s| {
7625 s.select_display_ranges([
7626 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7627 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7628 ]);
7629 });
7630 });
7631
7632 view.update(cx, |view, cx| {
7633 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7634 assert_eq!(
7635 view.selections.display_ranges(cx),
7636 &[
7637 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7638 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7639 ]
7640 );
7641 });
7642
7643 view.update(cx, |view, cx| {
7644 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7645 assert_eq!(
7646 view.selections.display_ranges(cx),
7647 &[
7648 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7649 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7650 ]
7651 );
7652 });
7653
7654 view.update(cx, |view, cx| {
7655 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7656 assert_eq!(
7657 view.selections.display_ranges(cx),
7658 &[
7659 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7660 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7661 ]
7662 );
7663 });
7664
7665 view.update(cx, |view, cx| {
7666 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7667 assert_eq!(
7668 view.selections.display_ranges(cx),
7669 &[
7670 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7671 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7672 ]
7673 );
7674 });
7675
7676 // Moving to the end of line again is a no-op.
7677 view.update(cx, |view, cx| {
7678 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7679 assert_eq!(
7680 view.selections.display_ranges(cx),
7681 &[
7682 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7683 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7684 ]
7685 );
7686 });
7687
7688 view.update(cx, |view, cx| {
7689 view.move_left(&MoveLeft, cx);
7690 view.select_to_beginning_of_line(
7691 &SelectToBeginningOfLine {
7692 stop_at_soft_wraps: true,
7693 },
7694 cx,
7695 );
7696 assert_eq!(
7697 view.selections.display_ranges(cx),
7698 &[
7699 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7700 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7701 ]
7702 );
7703 });
7704
7705 view.update(cx, |view, cx| {
7706 view.select_to_beginning_of_line(
7707 &SelectToBeginningOfLine {
7708 stop_at_soft_wraps: true,
7709 },
7710 cx,
7711 );
7712 assert_eq!(
7713 view.selections.display_ranges(cx),
7714 &[
7715 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7716 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7717 ]
7718 );
7719 });
7720
7721 view.update(cx, |view, cx| {
7722 view.select_to_beginning_of_line(
7723 &SelectToBeginningOfLine {
7724 stop_at_soft_wraps: true,
7725 },
7726 cx,
7727 );
7728 assert_eq!(
7729 view.selections.display_ranges(cx),
7730 &[
7731 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7732 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7733 ]
7734 );
7735 });
7736
7737 view.update(cx, |view, cx| {
7738 view.select_to_end_of_line(
7739 &SelectToEndOfLine {
7740 stop_at_soft_wraps: true,
7741 },
7742 cx,
7743 );
7744 assert_eq!(
7745 view.selections.display_ranges(cx),
7746 &[
7747 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7748 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7749 ]
7750 );
7751 });
7752
7753 view.update(cx, |view, cx| {
7754 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7755 assert_eq!(view.display_text(cx), "ab\n de");
7756 assert_eq!(
7757 view.selections.display_ranges(cx),
7758 &[
7759 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7760 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7761 ]
7762 );
7763 });
7764
7765 view.update(cx, |view, cx| {
7766 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7767 assert_eq!(view.display_text(cx), "\n");
7768 assert_eq!(
7769 view.selections.display_ranges(cx),
7770 &[
7771 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7772 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7773 ]
7774 );
7775 });
7776 }
7777
7778 #[gpui::test]
7779 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7780 cx.set_global(Settings::test(cx));
7781 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7782 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7783 view.update(cx, |view, cx| {
7784 view.change_selections(None, cx, |s| {
7785 s.select_display_ranges([
7786 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7787 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7788 ])
7789 });
7790
7791 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7792 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7793
7794 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7795 assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx);
7796
7797 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7798 assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx);
7799
7800 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7801 assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7802
7803 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7804 assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx);
7805
7806 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7807 assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx);
7808
7809 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7810 assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
7811
7812 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7813 assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
7814
7815 view.move_right(&MoveRight, cx);
7816 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7817 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7818
7819 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7820 assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx);
7821
7822 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7823 assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
7824 });
7825 }
7826
7827 #[gpui::test]
7828 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7829 cx.set_global(Settings::test(cx));
7830 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7831 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7832
7833 view.update(cx, |view, cx| {
7834 view.set_wrap_width(Some(140.), cx);
7835 assert_eq!(
7836 view.display_text(cx),
7837 "use one::{\n two::three::\n four::five\n};"
7838 );
7839
7840 view.change_selections(None, cx, |s| {
7841 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7842 });
7843
7844 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7845 assert_eq!(
7846 view.selections.display_ranges(cx),
7847 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7848 );
7849
7850 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7851 assert_eq!(
7852 view.selections.display_ranges(cx),
7853 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7854 );
7855
7856 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7857 assert_eq!(
7858 view.selections.display_ranges(cx),
7859 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7860 );
7861
7862 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7863 assert_eq!(
7864 view.selections.display_ranges(cx),
7865 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7866 );
7867
7868 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7869 assert_eq!(
7870 view.selections.display_ranges(cx),
7871 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7872 );
7873
7874 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7875 assert_eq!(
7876 view.selections.display_ranges(cx),
7877 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7878 );
7879 });
7880 }
7881
7882 #[gpui::test]
7883 async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
7884 let mut cx = EditorTestContext::new(cx);
7885 cx.set_state("one «two threeˇ» four");
7886 cx.update_editor(|editor, cx| {
7887 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7888 assert_eq!(editor.text(cx), " four");
7889 });
7890 }
7891
7892 #[gpui::test]
7893 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7894 cx.set_global(Settings::test(cx));
7895 let buffer = MultiBuffer::build_simple("one two three four", cx);
7896 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7897
7898 view.update(cx, |view, cx| {
7899 view.change_selections(None, cx, |s| {
7900 s.select_display_ranges([
7901 // an empty selection - the preceding word fragment is deleted
7902 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7903 // characters selected - they are deleted
7904 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7905 ])
7906 });
7907 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7908 });
7909
7910 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7911
7912 view.update(cx, |view, cx| {
7913 view.change_selections(None, cx, |s| {
7914 s.select_display_ranges([
7915 // an empty selection - the following word fragment is deleted
7916 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7917 // characters selected - they are deleted
7918 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7919 ])
7920 });
7921 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7922 });
7923
7924 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7925 }
7926
7927 #[gpui::test]
7928 fn test_newline(cx: &mut gpui::MutableAppContext) {
7929 cx.set_global(Settings::test(cx));
7930 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7931 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7932
7933 view.update(cx, |view, cx| {
7934 view.change_selections(None, cx, |s| {
7935 s.select_display_ranges([
7936 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7937 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7938 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7939 ])
7940 });
7941
7942 view.newline(&Newline, cx);
7943 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7944 });
7945 }
7946
7947 #[gpui::test]
7948 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7949 cx.set_global(Settings::test(cx));
7950 let buffer = MultiBuffer::build_simple(
7951 "
7952 a
7953 b(
7954 X
7955 )
7956 c(
7957 X
7958 )
7959 "
7960 .unindent()
7961 .as_str(),
7962 cx,
7963 );
7964
7965 let (_, editor) = cx.add_window(Default::default(), |cx| {
7966 let mut editor = build_editor(buffer.clone(), cx);
7967 editor.change_selections(None, cx, |s| {
7968 s.select_ranges([
7969 Point::new(2, 4)..Point::new(2, 5),
7970 Point::new(5, 4)..Point::new(5, 5),
7971 ])
7972 });
7973 editor
7974 });
7975
7976 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7977 buffer.update(cx, |buffer, cx| {
7978 buffer.edit(
7979 [
7980 (Point::new(1, 2)..Point::new(3, 0), ""),
7981 (Point::new(4, 2)..Point::new(6, 0), ""),
7982 ],
7983 None,
7984 cx,
7985 );
7986 assert_eq!(
7987 buffer.read(cx).text(),
7988 "
7989 a
7990 b()
7991 c()
7992 "
7993 .unindent()
7994 );
7995 });
7996
7997 editor.update(cx, |editor, cx| {
7998 assert_eq!(
7999 editor.selections.ranges(cx),
8000 &[
8001 Point::new(1, 2)..Point::new(1, 2),
8002 Point::new(2, 2)..Point::new(2, 2),
8003 ],
8004 );
8005
8006 editor.newline(&Newline, cx);
8007 assert_eq!(
8008 editor.text(cx),
8009 "
8010 a
8011 b(
8012 )
8013 c(
8014 )
8015 "
8016 .unindent()
8017 );
8018
8019 // The selections are moved after the inserted newlines
8020 assert_eq!(
8021 editor.selections.ranges(cx),
8022 &[
8023 Point::new(2, 0)..Point::new(2, 0),
8024 Point::new(4, 0)..Point::new(4, 0),
8025 ],
8026 );
8027 });
8028 }
8029
8030 #[gpui::test]
8031 async fn test_newline_below(cx: &mut gpui::TestAppContext) {
8032 let mut cx = EditorTestContext::new(cx);
8033 cx.update(|cx| {
8034 cx.update_global::<Settings, _, _>(|settings, _| {
8035 settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
8036 });
8037 });
8038
8039 let language = Arc::new(
8040 Language::new(
8041 LanguageConfig::default(),
8042 Some(tree_sitter_rust::language()),
8043 )
8044 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8045 .unwrap(),
8046 );
8047 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8048
8049 cx.set_state(indoc! {"
8050 const a: ˇA = (
8051 (ˇ
8052 «const_functionˇ»(ˇ),
8053 so«mˇ»et«hˇ»ing_ˇelse,ˇ
8054 )ˇ
8055 ˇ);ˇ
8056 "});
8057 cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
8058 cx.assert_editor_state(indoc! {"
8059 const a: A = (
8060 ˇ
8061 (
8062 ˇ
8063 const_function(),
8064 ˇ
8065 ˇ
8066 something_else,
8067 ˇ
8068 ˇ
8069 ˇ
8070 ˇ
8071 )
8072 ˇ
8073 );
8074 ˇ
8075 ˇ
8076 "});
8077 }
8078
8079 #[gpui::test]
8080 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
8081 cx.set_global(Settings::test(cx));
8082 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
8083 let (_, editor) = cx.add_window(Default::default(), |cx| {
8084 let mut editor = build_editor(buffer.clone(), cx);
8085 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
8086 editor
8087 });
8088
8089 // Edit the buffer directly, deleting ranges surrounding the editor's selections
8090 buffer.update(cx, |buffer, cx| {
8091 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
8092 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
8093 });
8094
8095 editor.update(cx, |editor, cx| {
8096 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
8097
8098 editor.insert("Z", cx);
8099 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
8100
8101 // The selections are moved after the inserted characters
8102 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
8103 });
8104 }
8105
8106 #[gpui::test]
8107 async fn test_tab(cx: &mut gpui::TestAppContext) {
8108 let mut cx = EditorTestContext::new(cx);
8109 cx.update(|cx| {
8110 cx.update_global::<Settings, _, _>(|settings, _| {
8111 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
8112 });
8113 });
8114 cx.set_state(indoc! {"
8115 ˇabˇc
8116 ˇ🏀ˇ🏀ˇefg
8117 dˇ
8118 "});
8119 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8120 cx.assert_editor_state(indoc! {"
8121 ˇab ˇc
8122 ˇ🏀 ˇ🏀 ˇefg
8123 d ˇ
8124 "});
8125
8126 cx.set_state(indoc! {"
8127 a
8128 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8129 "});
8130 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8131 cx.assert_editor_state(indoc! {"
8132 a
8133 «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
8134 "});
8135 }
8136
8137 #[gpui::test]
8138 async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
8139 let mut cx = EditorTestContext::new(cx);
8140 let language = Arc::new(
8141 Language::new(
8142 LanguageConfig::default(),
8143 Some(tree_sitter_rust::language()),
8144 )
8145 .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
8146 .unwrap(),
8147 );
8148 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8149
8150 // cursors that are already at the suggested indent level insert
8151 // a soft tab. cursors that are to the left of the suggested indent
8152 // auto-indent their line.
8153 cx.set_state(indoc! {"
8154 ˇ
8155 const a: B = (
8156 c(
8157 d(
8158 ˇ
8159 )
8160 ˇ
8161 ˇ )
8162 );
8163 "});
8164 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8165 cx.assert_editor_state(indoc! {"
8166 ˇ
8167 const a: B = (
8168 c(
8169 d(
8170 ˇ
8171 )
8172 ˇ
8173 ˇ)
8174 );
8175 "});
8176
8177 // handle auto-indent when there are multiple cursors on the same line
8178 cx.set_state(indoc! {"
8179 const a: B = (
8180 c(
8181 ˇ ˇ
8182 ˇ )
8183 );
8184 "});
8185 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8186 cx.assert_editor_state(indoc! {"
8187 const a: B = (
8188 c(
8189 ˇ
8190 ˇ)
8191 );
8192 "});
8193 }
8194
8195 #[gpui::test]
8196 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
8197 let mut cx = EditorTestContext::new(cx);
8198
8199 cx.set_state(indoc! {"
8200 «oneˇ» «twoˇ»
8201 three
8202 four
8203 "});
8204 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8205 cx.assert_editor_state(indoc! {"
8206 «oneˇ» «twoˇ»
8207 three
8208 four
8209 "});
8210
8211 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8212 cx.assert_editor_state(indoc! {"
8213 «oneˇ» «twoˇ»
8214 three
8215 four
8216 "});
8217
8218 // select across line ending
8219 cx.set_state(indoc! {"
8220 one two
8221 t«hree
8222 ˇ» four
8223 "});
8224 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8225 cx.assert_editor_state(indoc! {"
8226 one two
8227 t«hree
8228 ˇ» four
8229 "});
8230
8231 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8232 cx.assert_editor_state(indoc! {"
8233 one two
8234 t«hree
8235 ˇ» four
8236 "});
8237
8238 // Ensure that indenting/outdenting works when the cursor is at column 0.
8239 cx.set_state(indoc! {"
8240 one two
8241 ˇthree
8242 four
8243 "});
8244 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8245 cx.assert_editor_state(indoc! {"
8246 one two
8247 ˇthree
8248 four
8249 "});
8250
8251 cx.set_state(indoc! {"
8252 one two
8253 ˇ three
8254 four
8255 "});
8256 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8257 cx.assert_editor_state(indoc! {"
8258 one two
8259 ˇthree
8260 four
8261 "});
8262 }
8263
8264 #[gpui::test]
8265 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
8266 let mut cx = EditorTestContext::new(cx);
8267 cx.update(|cx| {
8268 cx.update_global::<Settings, _, _>(|settings, _| {
8269 settings.editor_overrides.hard_tabs = Some(true);
8270 });
8271 });
8272
8273 // select two ranges on one line
8274 cx.set_state(indoc! {"
8275 «oneˇ» «twoˇ»
8276 three
8277 four
8278 "});
8279 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8280 cx.assert_editor_state(indoc! {"
8281 \t«oneˇ» «twoˇ»
8282 three
8283 four
8284 "});
8285 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8286 cx.assert_editor_state(indoc! {"
8287 \t\t«oneˇ» «twoˇ»
8288 three
8289 four
8290 "});
8291 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8292 cx.assert_editor_state(indoc! {"
8293 \t«oneˇ» «twoˇ»
8294 three
8295 four
8296 "});
8297 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8298 cx.assert_editor_state(indoc! {"
8299 «oneˇ» «twoˇ»
8300 three
8301 four
8302 "});
8303
8304 // select across a line ending
8305 cx.set_state(indoc! {"
8306 one two
8307 t«hree
8308 ˇ»four
8309 "});
8310 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8311 cx.assert_editor_state(indoc! {"
8312 one two
8313 \tt«hree
8314 ˇ»four
8315 "});
8316 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8317 cx.assert_editor_state(indoc! {"
8318 one two
8319 \t\tt«hree
8320 ˇ»four
8321 "});
8322 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8323 cx.assert_editor_state(indoc! {"
8324 one two
8325 \tt«hree
8326 ˇ»four
8327 "});
8328 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8329 cx.assert_editor_state(indoc! {"
8330 one two
8331 t«hree
8332 ˇ»four
8333 "});
8334
8335 // Ensure that indenting/outdenting works when the cursor is at column 0.
8336 cx.set_state(indoc! {"
8337 one two
8338 ˇthree
8339 four
8340 "});
8341 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8342 cx.assert_editor_state(indoc! {"
8343 one two
8344 ˇthree
8345 four
8346 "});
8347 cx.update_editor(|e, cx| e.tab(&Tab, cx));
8348 cx.assert_editor_state(indoc! {"
8349 one two
8350 \tˇthree
8351 four
8352 "});
8353 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
8354 cx.assert_editor_state(indoc! {"
8355 one two
8356 ˇthree
8357 four
8358 "});
8359 }
8360
8361 #[gpui::test]
8362 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
8363 cx.set_global(
8364 Settings::test(cx)
8365 .with_language_defaults(
8366 "TOML",
8367 EditorSettings {
8368 tab_size: Some(2.try_into().unwrap()),
8369 ..Default::default()
8370 },
8371 )
8372 .with_language_defaults(
8373 "Rust",
8374 EditorSettings {
8375 tab_size: Some(4.try_into().unwrap()),
8376 ..Default::default()
8377 },
8378 ),
8379 );
8380 let toml_language = Arc::new(Language::new(
8381 LanguageConfig {
8382 name: "TOML".into(),
8383 ..Default::default()
8384 },
8385 None,
8386 ));
8387 let rust_language = Arc::new(Language::new(
8388 LanguageConfig {
8389 name: "Rust".into(),
8390 ..Default::default()
8391 },
8392 None,
8393 ));
8394
8395 let toml_buffer = cx
8396 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
8397 let rust_buffer = cx.add_model(|cx| {
8398 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
8399 });
8400 let multibuffer = cx.add_model(|cx| {
8401 let mut multibuffer = MultiBuffer::new(0);
8402 multibuffer.push_excerpts(
8403 toml_buffer.clone(),
8404 [ExcerptRange {
8405 context: Point::new(0, 0)..Point::new(2, 0),
8406 primary: None,
8407 }],
8408 cx,
8409 );
8410 multibuffer.push_excerpts(
8411 rust_buffer.clone(),
8412 [ExcerptRange {
8413 context: Point::new(0, 0)..Point::new(1, 0),
8414 primary: None,
8415 }],
8416 cx,
8417 );
8418 multibuffer
8419 });
8420
8421 cx.add_window(Default::default(), |cx| {
8422 let mut editor = build_editor(multibuffer, cx);
8423
8424 assert_eq!(
8425 editor.text(cx),
8426 indoc! {"
8427 a = 1
8428 b = 2
8429
8430 const c: usize = 3;
8431 "}
8432 );
8433
8434 select_ranges(
8435 &mut editor,
8436 indoc! {"
8437 «aˇ» = 1
8438 b = 2
8439
8440 «const c:ˇ» usize = 3;
8441 "},
8442 cx,
8443 );
8444
8445 editor.tab(&Tab, cx);
8446 assert_text_with_selections(
8447 &mut editor,
8448 indoc! {"
8449 «aˇ» = 1
8450 b = 2
8451
8452 «const c:ˇ» usize = 3;
8453 "},
8454 cx,
8455 );
8456 editor.tab_prev(&TabPrev, cx);
8457 assert_text_with_selections(
8458 &mut editor,
8459 indoc! {"
8460 «aˇ» = 1
8461 b = 2
8462
8463 «const c:ˇ» usize = 3;
8464 "},
8465 cx,
8466 );
8467
8468 editor
8469 });
8470 }
8471
8472 #[gpui::test]
8473 async fn test_backspace(cx: &mut gpui::TestAppContext) {
8474 let mut cx = EditorTestContext::new(cx);
8475
8476 // Basic backspace
8477 cx.set_state(indoc! {"
8478 onˇe two three
8479 fou«rˇ» five six
8480 seven «ˇeight nine
8481 »ten
8482 "});
8483 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8484 cx.assert_editor_state(indoc! {"
8485 oˇe two three
8486 fouˇ five six
8487 seven ˇten
8488 "});
8489
8490 // Test backspace inside and around indents
8491 cx.set_state(indoc! {"
8492 zero
8493 ˇone
8494 ˇtwo
8495 ˇ ˇ ˇ three
8496 ˇ ˇ four
8497 "});
8498 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8499 cx.assert_editor_state(indoc! {"
8500 zero
8501 ˇone
8502 ˇtwo
8503 ˇ threeˇ four
8504 "});
8505
8506 // Test backspace with line_mode set to true
8507 cx.update_editor(|e, _| e.selections.line_mode = true);
8508 cx.set_state(indoc! {"
8509 The ˇquick ˇbrown
8510 fox jumps over
8511 the lazy dog
8512 ˇThe qu«ick bˇ»rown"});
8513 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8514 cx.assert_editor_state(indoc! {"
8515 ˇfox jumps over
8516 the lazy dogˇ"});
8517 }
8518
8519 #[gpui::test]
8520 async fn test_delete(cx: &mut gpui::TestAppContext) {
8521 let mut cx = EditorTestContext::new(cx);
8522
8523 cx.set_state(indoc! {"
8524 onˇe two three
8525 fou«rˇ» five six
8526 seven «ˇeight nine
8527 »ten
8528 "});
8529 cx.update_editor(|e, cx| e.delete(&Delete, cx));
8530 cx.assert_editor_state(indoc! {"
8531 onˇ two three
8532 fouˇ five six
8533 seven ˇten
8534 "});
8535
8536 // Test backspace with line_mode set to true
8537 cx.update_editor(|e, _| e.selections.line_mode = true);
8538 cx.set_state(indoc! {"
8539 The ˇquick ˇbrown
8540 fox «ˇjum»ps over
8541 the lazy dog
8542 ˇThe qu«ick bˇ»rown"});
8543 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
8544 cx.assert_editor_state("ˇthe lazy dogˇ");
8545 }
8546
8547 #[gpui::test]
8548 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8549 cx.set_global(Settings::test(cx));
8550 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8551 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8552 view.update(cx, |view, cx| {
8553 view.change_selections(None, cx, |s| {
8554 s.select_display_ranges([
8555 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8556 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8557 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8558 ])
8559 });
8560 view.delete_line(&DeleteLine, cx);
8561 assert_eq!(view.display_text(cx), "ghi");
8562 assert_eq!(
8563 view.selections.display_ranges(cx),
8564 vec![
8565 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8566 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8567 ]
8568 );
8569 });
8570
8571 cx.set_global(Settings::test(cx));
8572 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8573 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8574 view.update(cx, |view, cx| {
8575 view.change_selections(None, cx, |s| {
8576 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8577 });
8578 view.delete_line(&DeleteLine, cx);
8579 assert_eq!(view.display_text(cx), "ghi\n");
8580 assert_eq!(
8581 view.selections.display_ranges(cx),
8582 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8583 );
8584 });
8585 }
8586
8587 #[gpui::test]
8588 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8589 cx.set_global(Settings::test(cx));
8590 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8591 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8592 view.update(cx, |view, cx| {
8593 view.change_selections(None, cx, |s| {
8594 s.select_display_ranges([
8595 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8596 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8597 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8598 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8599 ])
8600 });
8601 view.duplicate_line(&DuplicateLine, cx);
8602 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8603 assert_eq!(
8604 view.selections.display_ranges(cx),
8605 vec![
8606 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8607 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8608 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8609 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8610 ]
8611 );
8612 });
8613
8614 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8615 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8616 view.update(cx, |view, cx| {
8617 view.change_selections(None, cx, |s| {
8618 s.select_display_ranges([
8619 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8620 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8621 ])
8622 });
8623 view.duplicate_line(&DuplicateLine, cx);
8624 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8625 assert_eq!(
8626 view.selections.display_ranges(cx),
8627 vec![
8628 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8629 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8630 ]
8631 );
8632 });
8633 }
8634
8635 #[gpui::test]
8636 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8637 cx.set_global(Settings::test(cx));
8638 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8639 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8640 view.update(cx, |view, cx| {
8641 view.fold_ranges(
8642 vec![
8643 Point::new(0, 2)..Point::new(1, 2),
8644 Point::new(2, 3)..Point::new(4, 1),
8645 Point::new(7, 0)..Point::new(8, 4),
8646 ],
8647 cx,
8648 );
8649 view.change_selections(None, cx, |s| {
8650 s.select_display_ranges([
8651 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8652 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8653 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8654 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8655 ])
8656 });
8657 assert_eq!(
8658 view.display_text(cx),
8659 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8660 );
8661
8662 view.move_line_up(&MoveLineUp, cx);
8663 assert_eq!(
8664 view.display_text(cx),
8665 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8666 );
8667 assert_eq!(
8668 view.selections.display_ranges(cx),
8669 vec![
8670 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8671 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8672 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8673 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8674 ]
8675 );
8676 });
8677
8678 view.update(cx, |view, cx| {
8679 view.move_line_down(&MoveLineDown, cx);
8680 assert_eq!(
8681 view.display_text(cx),
8682 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8683 );
8684 assert_eq!(
8685 view.selections.display_ranges(cx),
8686 vec![
8687 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8688 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8689 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8690 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8691 ]
8692 );
8693 });
8694
8695 view.update(cx, |view, cx| {
8696 view.move_line_down(&MoveLineDown, cx);
8697 assert_eq!(
8698 view.display_text(cx),
8699 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8700 );
8701 assert_eq!(
8702 view.selections.display_ranges(cx),
8703 vec![
8704 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8705 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8706 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8707 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8708 ]
8709 );
8710 });
8711
8712 view.update(cx, |view, cx| {
8713 view.move_line_up(&MoveLineUp, cx);
8714 assert_eq!(
8715 view.display_text(cx),
8716 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8717 );
8718 assert_eq!(
8719 view.selections.display_ranges(cx),
8720 vec![
8721 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8722 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8723 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8724 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8725 ]
8726 );
8727 });
8728 }
8729
8730 #[gpui::test]
8731 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8732 cx.set_global(Settings::test(cx));
8733 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8734 let snapshot = buffer.read(cx).snapshot(cx);
8735 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8736 editor.update(cx, |editor, cx| {
8737 editor.insert_blocks(
8738 [BlockProperties {
8739 style: BlockStyle::Fixed,
8740 position: snapshot.anchor_after(Point::new(2, 0)),
8741 disposition: BlockDisposition::Below,
8742 height: 1,
8743 render: Arc::new(|_| Empty::new().boxed()),
8744 }],
8745 cx,
8746 );
8747 editor.change_selections(None, cx, |s| {
8748 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8749 });
8750 editor.move_line_down(&MoveLineDown, cx);
8751 });
8752 }
8753
8754 #[gpui::test]
8755 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8756 cx.set_global(Settings::test(cx));
8757
8758 _ = cx
8759 .add_window(Default::default(), |cx| {
8760 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8761
8762 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8763 editor.transpose(&Default::default(), cx);
8764 assert_eq!(editor.text(cx), "bac");
8765 assert_eq!(editor.selections.ranges(cx), [2..2]);
8766
8767 editor.transpose(&Default::default(), cx);
8768 assert_eq!(editor.text(cx), "bca");
8769 assert_eq!(editor.selections.ranges(cx), [3..3]);
8770
8771 editor.transpose(&Default::default(), cx);
8772 assert_eq!(editor.text(cx), "bac");
8773 assert_eq!(editor.selections.ranges(cx), [3..3]);
8774
8775 editor
8776 })
8777 .1;
8778
8779 _ = cx
8780 .add_window(Default::default(), |cx| {
8781 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8782
8783 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8784 editor.transpose(&Default::default(), cx);
8785 assert_eq!(editor.text(cx), "acb\nde");
8786 assert_eq!(editor.selections.ranges(cx), [3..3]);
8787
8788 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8789 editor.transpose(&Default::default(), cx);
8790 assert_eq!(editor.text(cx), "acbd\ne");
8791 assert_eq!(editor.selections.ranges(cx), [5..5]);
8792
8793 editor.transpose(&Default::default(), cx);
8794 assert_eq!(editor.text(cx), "acbde\n");
8795 assert_eq!(editor.selections.ranges(cx), [6..6]);
8796
8797 editor.transpose(&Default::default(), cx);
8798 assert_eq!(editor.text(cx), "acbd\ne");
8799 assert_eq!(editor.selections.ranges(cx), [6..6]);
8800
8801 editor
8802 })
8803 .1;
8804
8805 _ = cx
8806 .add_window(Default::default(), |cx| {
8807 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8808
8809 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8810 editor.transpose(&Default::default(), cx);
8811 assert_eq!(editor.text(cx), "bacd\ne");
8812 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8813
8814 editor.transpose(&Default::default(), cx);
8815 assert_eq!(editor.text(cx), "bcade\n");
8816 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8817
8818 editor.transpose(&Default::default(), cx);
8819 assert_eq!(editor.text(cx), "bcda\ne");
8820 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8821
8822 editor.transpose(&Default::default(), cx);
8823 assert_eq!(editor.text(cx), "bcade\n");
8824 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8825
8826 editor.transpose(&Default::default(), cx);
8827 assert_eq!(editor.text(cx), "bcaed\n");
8828 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8829
8830 editor
8831 })
8832 .1;
8833
8834 _ = cx
8835 .add_window(Default::default(), |cx| {
8836 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8837
8838 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8839 editor.transpose(&Default::default(), cx);
8840 assert_eq!(editor.text(cx), "🏀🍐✋");
8841 assert_eq!(editor.selections.ranges(cx), [8..8]);
8842
8843 editor.transpose(&Default::default(), cx);
8844 assert_eq!(editor.text(cx), "🏀✋🍐");
8845 assert_eq!(editor.selections.ranges(cx), [11..11]);
8846
8847 editor.transpose(&Default::default(), cx);
8848 assert_eq!(editor.text(cx), "🏀🍐✋");
8849 assert_eq!(editor.selections.ranges(cx), [11..11]);
8850
8851 editor
8852 })
8853 .1;
8854 }
8855
8856 #[gpui::test]
8857 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8858 let mut cx = EditorTestContext::new(cx);
8859
8860 cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
8861 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8862 cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
8863
8864 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8865 cx.set_state("two ˇfour ˇsix ˇ");
8866 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8867 cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
8868
8869 // Paste again but with only two cursors. Since the number of cursors doesn't
8870 // match the number of slices in the clipboard, the entire clipboard text
8871 // is pasted at each cursor.
8872 cx.set_state("ˇtwo one✅ four three six five ˇ");
8873 cx.update_editor(|e, cx| {
8874 e.handle_input("( ", cx);
8875 e.paste(&Paste, cx);
8876 e.handle_input(") ", cx);
8877 });
8878 cx.assert_editor_state(indoc! {"
8879 ( one✅
8880 three
8881 five ) ˇtwo one✅ four three six five ( one✅
8882 three
8883 five ) ˇ"});
8884
8885 // Cut with three selections, one of which is full-line.
8886 cx.set_state(indoc! {"
8887 1«2ˇ»3
8888 4ˇ567
8889 «8ˇ»9"});
8890 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8891 cx.assert_editor_state(indoc! {"
8892 1ˇ3
8893 ˇ9"});
8894
8895 // Paste with three selections, noticing how the copied selection that was full-line
8896 // gets inserted before the second cursor.
8897 cx.set_state(indoc! {"
8898 1ˇ3
8899 9ˇ
8900 «oˇ»ne"});
8901 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8902 cx.assert_editor_state(indoc! {"
8903 12ˇ3
8904 4567
8905 9ˇ
8906 8ˇne"});
8907
8908 // Copy with a single cursor only, which writes the whole line into the clipboard.
8909 cx.set_state(indoc! {"
8910 The quick brown
8911 fox juˇmps over
8912 the lazy dog"});
8913 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8914 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8915
8916 // Paste with three selections, noticing how the copied full-line selection is inserted
8917 // before the empty selections but replaces the selection that is non-empty.
8918 cx.set_state(indoc! {"
8919 Tˇhe quick brown
8920 «foˇ»x jumps over
8921 tˇhe lazy dog"});
8922 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8923 cx.assert_editor_state(indoc! {"
8924 fox jumps over
8925 Tˇhe quick brown
8926 fox jumps over
8927 ˇx jumps over
8928 fox jumps over
8929 tˇhe lazy dog"});
8930 }
8931
8932 #[gpui::test]
8933 async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
8934 let mut cx = EditorTestContext::new(cx);
8935 let language = Arc::new(Language::new(
8936 LanguageConfig::default(),
8937 Some(tree_sitter_rust::language()),
8938 ));
8939 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
8940
8941 // Cut an indented block, without the leading whitespace.
8942 cx.set_state(indoc! {"
8943 const a: B = (
8944 c(),
8945 «d(
8946 e,
8947 f
8948 )ˇ»
8949 );
8950 "});
8951 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8952 cx.assert_editor_state(indoc! {"
8953 const a: B = (
8954 c(),
8955 ˇ
8956 );
8957 "});
8958
8959 // Paste it at the same position.
8960 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8961 cx.assert_editor_state(indoc! {"
8962 const a: B = (
8963 c(),
8964 d(
8965 e,
8966 f
8967 )ˇ
8968 );
8969 "});
8970
8971 // Paste it at a line with a lower indent level.
8972 cx.set_state(indoc! {"
8973 ˇ
8974 const a: B = (
8975 c(),
8976 );
8977 "});
8978 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8979 cx.assert_editor_state(indoc! {"
8980 d(
8981 e,
8982 f
8983 )ˇ
8984 const a: B = (
8985 c(),
8986 );
8987 "});
8988
8989 // Cut an indented block, with the leading whitespace.
8990 cx.set_state(indoc! {"
8991 const a: B = (
8992 c(),
8993 « d(
8994 e,
8995 f
8996 )
8997 ˇ»);
8998 "});
8999 cx.update_editor(|e, cx| e.cut(&Cut, cx));
9000 cx.assert_editor_state(indoc! {"
9001 const a: B = (
9002 c(),
9003 ˇ);
9004 "});
9005
9006 // Paste it at the same position.
9007 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9008 cx.assert_editor_state(indoc! {"
9009 const a: B = (
9010 c(),
9011 d(
9012 e,
9013 f
9014 )
9015 ˇ);
9016 "});
9017
9018 // Paste it at a line with a higher indent level.
9019 cx.set_state(indoc! {"
9020 const a: B = (
9021 c(),
9022 d(
9023 e,
9024 fˇ
9025 )
9026 );
9027 "});
9028 cx.update_editor(|e, cx| e.paste(&Paste, cx));
9029 cx.assert_editor_state(indoc! {"
9030 const a: B = (
9031 c(),
9032 d(
9033 e,
9034 f d(
9035 e,
9036 f
9037 )
9038 ˇ
9039 )
9040 );
9041 "});
9042 }
9043
9044 #[gpui::test]
9045 fn test_select_all(cx: &mut gpui::MutableAppContext) {
9046 cx.set_global(Settings::test(cx));
9047 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
9048 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9049 view.update(cx, |view, cx| {
9050 view.select_all(&SelectAll, cx);
9051 assert_eq!(
9052 view.selections.display_ranges(cx),
9053 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
9054 );
9055 });
9056 }
9057
9058 #[gpui::test]
9059 fn test_select_line(cx: &mut gpui::MutableAppContext) {
9060 cx.set_global(Settings::test(cx));
9061 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
9062 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9063 view.update(cx, |view, cx| {
9064 view.change_selections(None, cx, |s| {
9065 s.select_display_ranges([
9066 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9067 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9068 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9069 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
9070 ])
9071 });
9072 view.select_line(&SelectLine, cx);
9073 assert_eq!(
9074 view.selections.display_ranges(cx),
9075 vec![
9076 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
9077 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
9078 ]
9079 );
9080 });
9081
9082 view.update(cx, |view, cx| {
9083 view.select_line(&SelectLine, cx);
9084 assert_eq!(
9085 view.selections.display_ranges(cx),
9086 vec![
9087 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
9088 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
9089 ]
9090 );
9091 });
9092
9093 view.update(cx, |view, cx| {
9094 view.select_line(&SelectLine, cx);
9095 assert_eq!(
9096 view.selections.display_ranges(cx),
9097 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
9098 );
9099 });
9100 }
9101
9102 #[gpui::test]
9103 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
9104 cx.set_global(Settings::test(cx));
9105 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
9106 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9107 view.update(cx, |view, cx| {
9108 view.fold_ranges(
9109 vec![
9110 Point::new(0, 2)..Point::new(1, 2),
9111 Point::new(2, 3)..Point::new(4, 1),
9112 Point::new(7, 0)..Point::new(8, 4),
9113 ],
9114 cx,
9115 );
9116 view.change_selections(None, cx, |s| {
9117 s.select_display_ranges([
9118 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9119 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9120 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9121 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9122 ])
9123 });
9124 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
9125 });
9126
9127 view.update(cx, |view, cx| {
9128 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9129 assert_eq!(
9130 view.display_text(cx),
9131 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
9132 );
9133 assert_eq!(
9134 view.selections.display_ranges(cx),
9135 [
9136 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
9137 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
9138 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
9139 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
9140 ]
9141 );
9142 });
9143
9144 view.update(cx, |view, cx| {
9145 view.change_selections(None, cx, |s| {
9146 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
9147 });
9148 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
9149 assert_eq!(
9150 view.display_text(cx),
9151 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
9152 );
9153 assert_eq!(
9154 view.selections.display_ranges(cx),
9155 [
9156 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
9157 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
9158 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9159 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
9160 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
9161 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
9162 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
9163 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
9164 ]
9165 );
9166 });
9167 }
9168
9169 #[gpui::test]
9170 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
9171 cx.set_global(Settings::test(cx));
9172 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
9173 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
9174
9175 view.update(cx, |view, cx| {
9176 view.change_selections(None, cx, |s| {
9177 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
9178 });
9179 });
9180 view.update(cx, |view, cx| {
9181 view.add_selection_above(&AddSelectionAbove, cx);
9182 assert_eq!(
9183 view.selections.display_ranges(cx),
9184 vec![
9185 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9186 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9187 ]
9188 );
9189 });
9190
9191 view.update(cx, |view, cx| {
9192 view.add_selection_above(&AddSelectionAbove, cx);
9193 assert_eq!(
9194 view.selections.display_ranges(cx),
9195 vec![
9196 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9197 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9198 ]
9199 );
9200 });
9201
9202 view.update(cx, |view, cx| {
9203 view.add_selection_below(&AddSelectionBelow, cx);
9204 assert_eq!(
9205 view.selections.display_ranges(cx),
9206 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9207 );
9208
9209 view.undo_selection(&UndoSelection, cx);
9210 assert_eq!(
9211 view.selections.display_ranges(cx),
9212 vec![
9213 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
9214 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
9215 ]
9216 );
9217
9218 view.redo_selection(&RedoSelection, cx);
9219 assert_eq!(
9220 view.selections.display_ranges(cx),
9221 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
9222 );
9223 });
9224
9225 view.update(cx, |view, cx| {
9226 view.add_selection_below(&AddSelectionBelow, cx);
9227 assert_eq!(
9228 view.selections.display_ranges(cx),
9229 vec![
9230 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9231 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9232 ]
9233 );
9234 });
9235
9236 view.update(cx, |view, cx| {
9237 view.add_selection_below(&AddSelectionBelow, cx);
9238 assert_eq!(
9239 view.selections.display_ranges(cx),
9240 vec![
9241 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
9242 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
9243 ]
9244 );
9245 });
9246
9247 view.update(cx, |view, cx| {
9248 view.change_selections(None, cx, |s| {
9249 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
9250 });
9251 });
9252 view.update(cx, |view, cx| {
9253 view.add_selection_below(&AddSelectionBelow, cx);
9254 assert_eq!(
9255 view.selections.display_ranges(cx),
9256 vec![
9257 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9258 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9259 ]
9260 );
9261 });
9262
9263 view.update(cx, |view, cx| {
9264 view.add_selection_below(&AddSelectionBelow, cx);
9265 assert_eq!(
9266 view.selections.display_ranges(cx),
9267 vec![
9268 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
9269 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
9270 ]
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.add_selection_above(&AddSelectionAbove, cx);
9284 assert_eq!(
9285 view.selections.display_ranges(cx),
9286 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
9287 );
9288 });
9289
9290 view.update(cx, |view, cx| {
9291 view.change_selections(None, cx, |s| {
9292 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
9293 });
9294 view.add_selection_below(&AddSelectionBelow, cx);
9295 assert_eq!(
9296 view.selections.display_ranges(cx),
9297 vec![
9298 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9299 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9300 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9301 ]
9302 );
9303 });
9304
9305 view.update(cx, |view, cx| {
9306 view.add_selection_below(&AddSelectionBelow, cx);
9307 assert_eq!(
9308 view.selections.display_ranges(cx),
9309 vec![
9310 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9311 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9312 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9313 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
9314 ]
9315 );
9316 });
9317
9318 view.update(cx, |view, cx| {
9319 view.add_selection_above(&AddSelectionAbove, cx);
9320 assert_eq!(
9321 view.selections.display_ranges(cx),
9322 vec![
9323 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
9324 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
9325 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
9326 ]
9327 );
9328 });
9329
9330 view.update(cx, |view, cx| {
9331 view.change_selections(None, cx, |s| {
9332 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
9333 });
9334 });
9335 view.update(cx, |view, cx| {
9336 view.add_selection_above(&AddSelectionAbove, cx);
9337 assert_eq!(
9338 view.selections.display_ranges(cx),
9339 vec![
9340 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
9341 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9342 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9343 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9344 ]
9345 );
9346 });
9347
9348 view.update(cx, |view, cx| {
9349 view.add_selection_below(&AddSelectionBelow, cx);
9350 assert_eq!(
9351 view.selections.display_ranges(cx),
9352 vec![
9353 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
9354 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
9355 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
9356 ]
9357 );
9358 });
9359 }
9360
9361 #[gpui::test]
9362 async fn test_select_next(cx: &mut gpui::TestAppContext) {
9363 let mut cx = EditorTestContext::new(cx);
9364 cx.set_state("abc\nˇabc abc\ndefabc\nabc");
9365
9366 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9367 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9368
9369 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9370 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9371
9372 cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
9373 cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
9374
9375 cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
9376 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
9377
9378 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9379 cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9380
9381 cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
9382 cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
9383 }
9384
9385 #[gpui::test]
9386 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
9387 cx.update(|cx| cx.set_global(Settings::test(cx)));
9388 let language = Arc::new(Language::new(
9389 LanguageConfig::default(),
9390 Some(tree_sitter_rust::language()),
9391 ));
9392
9393 let text = r#"
9394 use mod1::mod2::{mod3, mod4};
9395
9396 fn fn_1(param1: bool, param2: &str) {
9397 let var1 = "text";
9398 }
9399 "#
9400 .unindent();
9401
9402 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9403 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9404 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9405 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9406 .await;
9407
9408 view.update(cx, |view, cx| {
9409 view.change_selections(None, cx, |s| {
9410 s.select_display_ranges([
9411 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9412 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9413 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9414 ]);
9415 });
9416 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9417 });
9418 assert_eq!(
9419 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
9420 &[
9421 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9422 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9423 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9424 ]
9425 );
9426
9427 view.update(cx, |view, cx| {
9428 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9429 });
9430 assert_eq!(
9431 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9432 &[
9433 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9434 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9435 ]
9436 );
9437
9438 view.update(cx, |view, cx| {
9439 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9440 });
9441 assert_eq!(
9442 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9443 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9444 );
9445
9446 // Trying to expand the selected syntax node one more time has no effect.
9447 view.update(cx, |view, cx| {
9448 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9449 });
9450 assert_eq!(
9451 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9452 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
9453 );
9454
9455 view.update(cx, |view, cx| {
9456 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9457 });
9458 assert_eq!(
9459 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9460 &[
9461 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9462 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
9463 ]
9464 );
9465
9466 view.update(cx, |view, cx| {
9467 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9468 });
9469 assert_eq!(
9470 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9471 &[
9472 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
9473 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9474 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
9475 ]
9476 );
9477
9478 view.update(cx, |view, cx| {
9479 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9480 });
9481 assert_eq!(
9482 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9483 &[
9484 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9485 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9486 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9487 ]
9488 );
9489
9490 // Trying to shrink the selected syntax node one more time has no effect.
9491 view.update(cx, |view, cx| {
9492 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
9493 });
9494 assert_eq!(
9495 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9496 &[
9497 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
9498 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
9499 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
9500 ]
9501 );
9502
9503 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9504 // a fold.
9505 view.update(cx, |view, cx| {
9506 view.fold_ranges(
9507 vec![
9508 Point::new(0, 21)..Point::new(0, 24),
9509 Point::new(3, 20)..Point::new(3, 22),
9510 ],
9511 cx,
9512 );
9513 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9514 });
9515 assert_eq!(
9516 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
9517 &[
9518 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9519 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9520 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9521 ]
9522 );
9523 }
9524
9525 #[gpui::test]
9526 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9527 cx.update(|cx| cx.set_global(Settings::test(cx)));
9528 let language = Arc::new(
9529 Language::new(
9530 LanguageConfig {
9531 brackets: vec![
9532 BracketPair {
9533 start: "{".to_string(),
9534 end: "}".to_string(),
9535 close: false,
9536 newline: true,
9537 },
9538 BracketPair {
9539 start: "(".to_string(),
9540 end: ")".to_string(),
9541 close: false,
9542 newline: true,
9543 },
9544 ],
9545 ..Default::default()
9546 },
9547 Some(tree_sitter_rust::language()),
9548 )
9549 .with_indents_query(
9550 r#"
9551 (_ "(" ")" @end) @indent
9552 (_ "{" "}" @end) @indent
9553 "#,
9554 )
9555 .unwrap(),
9556 );
9557
9558 let text = "fn a() {}";
9559
9560 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9561 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9562 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9563 editor
9564 .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9565 .await;
9566
9567 editor.update(cx, |editor, cx| {
9568 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
9569 editor.newline(&Newline, cx);
9570 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9571 assert_eq!(
9572 editor.selections.ranges(cx),
9573 &[
9574 Point::new(1, 4)..Point::new(1, 4),
9575 Point::new(3, 4)..Point::new(3, 4),
9576 Point::new(5, 0)..Point::new(5, 0)
9577 ]
9578 );
9579 });
9580 }
9581
9582 #[gpui::test]
9583 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9584 cx.update(|cx| cx.set_global(Settings::test(cx)));
9585 let language = Arc::new(Language::new(
9586 LanguageConfig {
9587 brackets: vec![
9588 BracketPair {
9589 start: "{".to_string(),
9590 end: "}".to_string(),
9591 close: true,
9592 newline: true,
9593 },
9594 BracketPair {
9595 start: "/*".to_string(),
9596 end: " */".to_string(),
9597 close: true,
9598 newline: true,
9599 },
9600 BracketPair {
9601 start: "[".to_string(),
9602 end: "]".to_string(),
9603 close: false,
9604 newline: true,
9605 },
9606 ],
9607 autoclose_before: "})]".to_string(),
9608 ..Default::default()
9609 },
9610 Some(tree_sitter_rust::language()),
9611 ));
9612
9613 let text = r#"
9614 a
9615
9616 /
9617
9618 "#
9619 .unindent();
9620
9621 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9622 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9623 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9624 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9625 .await;
9626
9627 view.update(cx, |view, cx| {
9628 view.change_selections(None, cx, |s| {
9629 s.select_display_ranges([
9630 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9631 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9632 ])
9633 });
9634
9635 view.handle_input("{", cx);
9636 view.handle_input("{", cx);
9637 view.handle_input("{", cx);
9638 assert_eq!(
9639 view.text(cx),
9640 "
9641 {{{}}}
9642 {{{}}}
9643 /
9644
9645 "
9646 .unindent()
9647 );
9648
9649 view.move_right(&MoveRight, cx);
9650 view.handle_input("}", cx);
9651 view.handle_input("}", cx);
9652 view.handle_input("}", cx);
9653 assert_eq!(
9654 view.text(cx),
9655 "
9656 {{{}}}}
9657 {{{}}}}
9658 /
9659
9660 "
9661 .unindent()
9662 );
9663
9664 view.undo(&Undo, cx);
9665 view.handle_input("/", cx);
9666 view.handle_input("*", cx);
9667 assert_eq!(
9668 view.text(cx),
9669 "
9670 /* */
9671 /* */
9672 /
9673
9674 "
9675 .unindent()
9676 );
9677
9678 view.undo(&Undo, cx);
9679 view.change_selections(None, cx, |s| {
9680 s.select_display_ranges([
9681 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9682 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9683 ])
9684 });
9685 view.handle_input("*", cx);
9686 assert_eq!(
9687 view.text(cx),
9688 "
9689 a
9690
9691 /*
9692 *
9693 "
9694 .unindent()
9695 );
9696
9697 // Don't autoclose if the next character isn't whitespace and isn't
9698 // listed in the language's "autoclose_before" section.
9699 view.finalize_last_transaction(cx);
9700 view.change_selections(None, cx, |s| {
9701 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9702 });
9703 view.handle_input("{", cx);
9704 assert_eq!(
9705 view.text(cx),
9706 "
9707 {a
9708
9709 /*
9710 *
9711 "
9712 .unindent()
9713 );
9714
9715 view.undo(&Undo, cx);
9716 view.change_selections(None, cx, |s| {
9717 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9718 });
9719 view.handle_input("{", cx);
9720 assert_eq!(
9721 view.text(cx),
9722 "
9723 {a}
9724
9725 /*
9726 *
9727 "
9728 .unindent()
9729 );
9730 assert_eq!(
9731 view.selections.display_ranges(cx),
9732 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9733 );
9734
9735 view.undo(&Undo, cx);
9736 view.handle_input("[", cx);
9737 assert_eq!(
9738 view.text(cx),
9739 "
9740 [a]
9741
9742 /*
9743 *
9744 "
9745 .unindent()
9746 );
9747 assert_eq!(
9748 view.selections.display_ranges(cx),
9749 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9750 );
9751
9752 view.undo(&Undo, cx);
9753 view.change_selections(None, cx, |s| {
9754 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9755 });
9756 view.handle_input("[", cx);
9757 assert_eq!(
9758 view.text(cx),
9759 "
9760 a[
9761
9762 /*
9763 *
9764 "
9765 .unindent()
9766 );
9767 assert_eq!(
9768 view.selections.display_ranges(cx),
9769 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9770 );
9771 });
9772 }
9773
9774 #[gpui::test]
9775 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9776 cx.update(|cx| cx.set_global(Settings::test(cx)));
9777 let language = Arc::new(Language::new(
9778 LanguageConfig {
9779 brackets: vec![BracketPair {
9780 start: "{".to_string(),
9781 end: "}".to_string(),
9782 close: true,
9783 newline: true,
9784 }],
9785 ..Default::default()
9786 },
9787 Some(tree_sitter_rust::language()),
9788 ));
9789
9790 let text = r#"
9791 a
9792 b
9793 c
9794 "#
9795 .unindent();
9796
9797 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9798 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9799 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9800 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9801 .await;
9802
9803 view.update(cx, |view, cx| {
9804 view.change_selections(None, cx, |s| {
9805 s.select_display_ranges([
9806 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9807 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9808 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9809 ])
9810 });
9811
9812 view.handle_input("{", cx);
9813 view.handle_input("{", cx);
9814 view.handle_input("{", cx);
9815 assert_eq!(
9816 view.text(cx),
9817 "
9818 {{{a}}}
9819 {{{b}}}
9820 {{{c}}}
9821 "
9822 .unindent()
9823 );
9824 assert_eq!(
9825 view.selections.display_ranges(cx),
9826 [
9827 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9828 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9829 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9830 ]
9831 );
9832
9833 view.undo(&Undo, cx);
9834 assert_eq!(
9835 view.text(cx),
9836 "
9837 a
9838 b
9839 c
9840 "
9841 .unindent()
9842 );
9843 assert_eq!(
9844 view.selections.display_ranges(cx),
9845 [
9846 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9847 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9848 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9849 ]
9850 );
9851 });
9852 }
9853
9854 #[gpui::test]
9855 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9856 cx.update(|cx| cx.set_global(Settings::test(cx)));
9857 let language = Arc::new(Language::new(
9858 LanguageConfig {
9859 brackets: vec![BracketPair {
9860 start: "{".to_string(),
9861 end: "}".to_string(),
9862 close: true,
9863 newline: true,
9864 }],
9865 autoclose_before: "}".to_string(),
9866 ..Default::default()
9867 },
9868 Some(tree_sitter_rust::language()),
9869 ));
9870
9871 let text = r#"
9872 a
9873 b
9874 c
9875 "#
9876 .unindent();
9877
9878 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9879 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9880 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9881 editor
9882 .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9883 .await;
9884
9885 editor.update(cx, |editor, cx| {
9886 editor.change_selections(None, cx, |s| {
9887 s.select_ranges([
9888 Point::new(0, 1)..Point::new(0, 1),
9889 Point::new(1, 1)..Point::new(1, 1),
9890 Point::new(2, 1)..Point::new(2, 1),
9891 ])
9892 });
9893
9894 editor.handle_input("{", cx);
9895 editor.handle_input("{", cx);
9896 editor.handle_input("_", cx);
9897 assert_eq!(
9898 editor.text(cx),
9899 "
9900 a{{_}}
9901 b{{_}}
9902 c{{_}}
9903 "
9904 .unindent()
9905 );
9906 assert_eq!(
9907 editor.selections.ranges::<Point>(cx),
9908 [
9909 Point::new(0, 4)..Point::new(0, 4),
9910 Point::new(1, 4)..Point::new(1, 4),
9911 Point::new(2, 4)..Point::new(2, 4)
9912 ]
9913 );
9914
9915 editor.backspace(&Default::default(), cx);
9916 editor.backspace(&Default::default(), cx);
9917 assert_eq!(
9918 editor.text(cx),
9919 "
9920 a{}
9921 b{}
9922 c{}
9923 "
9924 .unindent()
9925 );
9926 assert_eq!(
9927 editor.selections.ranges::<Point>(cx),
9928 [
9929 Point::new(0, 2)..Point::new(0, 2),
9930 Point::new(1, 2)..Point::new(1, 2),
9931 Point::new(2, 2)..Point::new(2, 2)
9932 ]
9933 );
9934
9935 editor.delete_to_previous_word_start(&Default::default(), cx);
9936 assert_eq!(
9937 editor.text(cx),
9938 "
9939 a
9940 b
9941 c
9942 "
9943 .unindent()
9944 );
9945 assert_eq!(
9946 editor.selections.ranges::<Point>(cx),
9947 [
9948 Point::new(0, 1)..Point::new(0, 1),
9949 Point::new(1, 1)..Point::new(1, 1),
9950 Point::new(2, 1)..Point::new(2, 1)
9951 ]
9952 );
9953 });
9954 }
9955
9956 #[gpui::test]
9957 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9958 cx.update(|cx| cx.set_global(Settings::test(cx)));
9959
9960 let (text, insertion_ranges) = marked_text_ranges(
9961 indoc! {"
9962 a.ˇ b
9963 a.ˇ b
9964 a.ˇ b
9965 "},
9966 false,
9967 );
9968
9969 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9970 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9971
9972 editor.update(cx, |editor, cx| {
9973 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9974
9975 editor
9976 .insert_snippet(&insertion_ranges, snippet, cx)
9977 .unwrap();
9978
9979 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
9980 let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
9981 assert_eq!(editor.text(cx), expected_text);
9982 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9983 }
9984
9985 assert(
9986 editor,
9987 cx,
9988 indoc! {"
9989 a.f(«one», two, «three») b
9990 a.f(«one», two, «three») b
9991 a.f(«one», two, «three») b
9992 "},
9993 );
9994
9995 // Can't move earlier than the first tab stop
9996 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9997 assert(
9998 editor,
9999 cx,
10000 indoc! {"
10001 a.f(«one», two, «three») b
10002 a.f(«one», two, «three») b
10003 a.f(«one», two, «three») b
10004 "},
10005 );
10006
10007 assert!(editor.move_to_next_snippet_tabstop(cx));
10008 assert(
10009 editor,
10010 cx,
10011 indoc! {"
10012 a.f(one, «two», three) b
10013 a.f(one, «two», three) b
10014 a.f(one, «two», three) b
10015 "},
10016 );
10017
10018 editor.move_to_prev_snippet_tabstop(cx);
10019 assert(
10020 editor,
10021 cx,
10022 indoc! {"
10023 a.f(«one», two, «three») b
10024 a.f(«one», two, «three») b
10025 a.f(«one», two, «three») b
10026 "},
10027 );
10028
10029 assert!(editor.move_to_next_snippet_tabstop(cx));
10030 assert(
10031 editor,
10032 cx,
10033 indoc! {"
10034 a.f(one, «two», three) b
10035 a.f(one, «two», three) b
10036 a.f(one, «two», three) b
10037 "},
10038 );
10039 assert!(editor.move_to_next_snippet_tabstop(cx));
10040 assert(
10041 editor,
10042 cx,
10043 indoc! {"
10044 a.f(one, two, three)ˇ b
10045 a.f(one, two, three)ˇ b
10046 a.f(one, two, three)ˇ b
10047 "},
10048 );
10049
10050 // As soon as the last tab stop is reached, snippet state is gone
10051 editor.move_to_prev_snippet_tabstop(cx);
10052 assert(
10053 editor,
10054 cx,
10055 indoc! {"
10056 a.f(one, two, three)ˇ b
10057 a.f(one, two, three)ˇ b
10058 a.f(one, two, three)ˇ b
10059 "},
10060 );
10061 });
10062 }
10063
10064 #[gpui::test]
10065 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
10066 cx.foreground().forbid_parking();
10067
10068 let mut language = Language::new(
10069 LanguageConfig {
10070 name: "Rust".into(),
10071 path_suffixes: vec!["rs".to_string()],
10072 ..Default::default()
10073 },
10074 Some(tree_sitter_rust::language()),
10075 );
10076 let mut fake_servers = language
10077 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10078 capabilities: lsp::ServerCapabilities {
10079 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10080 ..Default::default()
10081 },
10082 ..Default::default()
10083 }))
10084 .await;
10085
10086 let fs = FakeFs::new(cx.background());
10087 fs.insert_file("/file.rs", Default::default()).await;
10088
10089 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10090 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10091 let buffer = project
10092 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10093 .await
10094 .unwrap();
10095
10096 cx.foreground().start_waiting();
10097 let fake_server = fake_servers.next().await.unwrap();
10098
10099 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10100 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10101 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10102 assert!(cx.read(|cx| editor.is_dirty(cx)));
10103
10104 let save = cx.update(|cx| editor.save(project.clone(), cx));
10105 fake_server
10106 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10107 assert_eq!(
10108 params.text_document.uri,
10109 lsp::Url::from_file_path("/file.rs").unwrap()
10110 );
10111 assert_eq!(params.options.tab_size, 4);
10112 Ok(Some(vec![lsp::TextEdit::new(
10113 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10114 ", ".to_string(),
10115 )]))
10116 })
10117 .next()
10118 .await;
10119 cx.foreground().start_waiting();
10120 save.await.unwrap();
10121 assert_eq!(
10122 editor.read_with(cx, |editor, cx| editor.text(cx)),
10123 "one, two\nthree\n"
10124 );
10125 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10126
10127 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10128 assert!(cx.read(|cx| editor.is_dirty(cx)));
10129
10130 // Ensure we can still save even if formatting hangs.
10131 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10132 assert_eq!(
10133 params.text_document.uri,
10134 lsp::Url::from_file_path("/file.rs").unwrap()
10135 );
10136 futures::future::pending::<()>().await;
10137 unreachable!()
10138 });
10139 let save = cx.update(|cx| editor.save(project.clone(), cx));
10140 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10141 cx.foreground().start_waiting();
10142 save.await.unwrap();
10143 assert_eq!(
10144 editor.read_with(cx, |editor, cx| editor.text(cx)),
10145 "one\ntwo\nthree\n"
10146 );
10147 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10148
10149 // Set rust language override and assert overriden tabsize is sent to language server
10150 cx.update(|cx| {
10151 cx.update_global::<Settings, _, _>(|settings, _| {
10152 settings.language_overrides.insert(
10153 "Rust".into(),
10154 EditorSettings {
10155 tab_size: Some(8.try_into().unwrap()),
10156 ..Default::default()
10157 },
10158 );
10159 })
10160 });
10161
10162 let save = cx.update(|cx| editor.save(project.clone(), cx));
10163 fake_server
10164 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10165 assert_eq!(
10166 params.text_document.uri,
10167 lsp::Url::from_file_path("/file.rs").unwrap()
10168 );
10169 assert_eq!(params.options.tab_size, 8);
10170 Ok(Some(vec![]))
10171 })
10172 .next()
10173 .await;
10174 cx.foreground().start_waiting();
10175 save.await.unwrap();
10176 }
10177
10178 #[gpui::test]
10179 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
10180 cx.foreground().forbid_parking();
10181
10182 let mut language = Language::new(
10183 LanguageConfig {
10184 name: "Rust".into(),
10185 path_suffixes: vec!["rs".to_string()],
10186 ..Default::default()
10187 },
10188 Some(tree_sitter_rust::language()),
10189 );
10190 let mut fake_servers = language
10191 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10192 capabilities: lsp::ServerCapabilities {
10193 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
10194 ..Default::default()
10195 },
10196 ..Default::default()
10197 }))
10198 .await;
10199
10200 let fs = FakeFs::new(cx.background());
10201 fs.insert_file("/file.rs", Default::default()).await;
10202
10203 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10204 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10205 let buffer = project
10206 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10207 .await
10208 .unwrap();
10209
10210 cx.foreground().start_waiting();
10211 let fake_server = fake_servers.next().await.unwrap();
10212
10213 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10214 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10215 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10216 assert!(cx.read(|cx| editor.is_dirty(cx)));
10217
10218 let save = cx.update(|cx| editor.save(project.clone(), cx));
10219 fake_server
10220 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10221 assert_eq!(
10222 params.text_document.uri,
10223 lsp::Url::from_file_path("/file.rs").unwrap()
10224 );
10225 assert_eq!(params.options.tab_size, 4);
10226 Ok(Some(vec![lsp::TextEdit::new(
10227 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10228 ", ".to_string(),
10229 )]))
10230 })
10231 .next()
10232 .await;
10233 cx.foreground().start_waiting();
10234 save.await.unwrap();
10235 assert_eq!(
10236 editor.read_with(cx, |editor, cx| editor.text(cx)),
10237 "one, two\nthree\n"
10238 );
10239 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10240
10241 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10242 assert!(cx.read(|cx| editor.is_dirty(cx)));
10243
10244 // Ensure we can still save even if formatting hangs.
10245 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
10246 move |params, _| async move {
10247 assert_eq!(
10248 params.text_document.uri,
10249 lsp::Url::from_file_path("/file.rs").unwrap()
10250 );
10251 futures::future::pending::<()>().await;
10252 unreachable!()
10253 },
10254 );
10255 let save = cx.update(|cx| editor.save(project.clone(), cx));
10256 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10257 cx.foreground().start_waiting();
10258 save.await.unwrap();
10259 assert_eq!(
10260 editor.read_with(cx, |editor, cx| editor.text(cx)),
10261 "one\ntwo\nthree\n"
10262 );
10263 assert!(!cx.read(|cx| editor.is_dirty(cx)));
10264
10265 // Set rust language override and assert overriden tabsize is sent to language server
10266 cx.update(|cx| {
10267 cx.update_global::<Settings, _, _>(|settings, _| {
10268 settings.language_overrides.insert(
10269 "Rust".into(),
10270 EditorSettings {
10271 tab_size: Some(8.try_into().unwrap()),
10272 ..Default::default()
10273 },
10274 );
10275 })
10276 });
10277
10278 let save = cx.update(|cx| editor.save(project.clone(), cx));
10279 fake_server
10280 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
10281 assert_eq!(
10282 params.text_document.uri,
10283 lsp::Url::from_file_path("/file.rs").unwrap()
10284 );
10285 assert_eq!(params.options.tab_size, 8);
10286 Ok(Some(vec![]))
10287 })
10288 .next()
10289 .await;
10290 cx.foreground().start_waiting();
10291 save.await.unwrap();
10292 }
10293
10294 #[gpui::test]
10295 async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
10296 cx.foreground().forbid_parking();
10297
10298 let mut language = Language::new(
10299 LanguageConfig {
10300 name: "Rust".into(),
10301 path_suffixes: vec!["rs".to_string()],
10302 ..Default::default()
10303 },
10304 Some(tree_sitter_rust::language()),
10305 );
10306 let mut fake_servers = language
10307 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
10308 capabilities: lsp::ServerCapabilities {
10309 document_formatting_provider: Some(lsp::OneOf::Left(true)),
10310 ..Default::default()
10311 },
10312 ..Default::default()
10313 }))
10314 .await;
10315
10316 let fs = FakeFs::new(cx.background());
10317 fs.insert_file("/file.rs", Default::default()).await;
10318
10319 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10320 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
10321 let buffer = project
10322 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
10323 .await
10324 .unwrap();
10325
10326 cx.foreground().start_waiting();
10327 let fake_server = fake_servers.next().await.unwrap();
10328
10329 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10330 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
10331 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10332
10333 let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
10334 fake_server
10335 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10336 assert_eq!(
10337 params.text_document.uri,
10338 lsp::Url::from_file_path("/file.rs").unwrap()
10339 );
10340 assert_eq!(params.options.tab_size, 4);
10341 Ok(Some(vec![lsp::TextEdit::new(
10342 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
10343 ", ".to_string(),
10344 )]))
10345 })
10346 .next()
10347 .await;
10348 cx.foreground().start_waiting();
10349 format.await.unwrap();
10350 assert_eq!(
10351 editor.read_with(cx, |editor, cx| editor.text(cx)),
10352 "one, two\nthree\n"
10353 );
10354
10355 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
10356 // Ensure we don't lock if formatting hangs.
10357 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
10358 assert_eq!(
10359 params.text_document.uri,
10360 lsp::Url::from_file_path("/file.rs").unwrap()
10361 );
10362 futures::future::pending::<()>().await;
10363 unreachable!()
10364 });
10365 let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
10366 cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
10367 cx.foreground().start_waiting();
10368 format.await.unwrap();
10369 assert_eq!(
10370 editor.read_with(cx, |editor, cx| editor.text(cx)),
10371 "one\ntwo\nthree\n"
10372 );
10373 }
10374
10375 #[gpui::test]
10376 async fn test_completion(cx: &mut gpui::TestAppContext) {
10377 let mut cx = EditorLspTestContext::new_rust(
10378 lsp::ServerCapabilities {
10379 completion_provider: Some(lsp::CompletionOptions {
10380 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10381 ..Default::default()
10382 }),
10383 ..Default::default()
10384 },
10385 cx,
10386 )
10387 .await;
10388
10389 cx.set_state(indoc! {"
10390 oneˇ
10391 two
10392 three
10393 "});
10394 cx.simulate_keystroke(".");
10395 handle_completion_request(
10396 &mut cx,
10397 indoc! {"
10398 one.|<>
10399 two
10400 three
10401 "},
10402 vec!["first_completion", "second_completion"],
10403 )
10404 .await;
10405 cx.condition(|editor, _| editor.context_menu_visible())
10406 .await;
10407 let apply_additional_edits = cx.update_editor(|editor, cx| {
10408 editor.move_down(&MoveDown, cx);
10409 editor
10410 .confirm_completion(&ConfirmCompletion::default(), cx)
10411 .unwrap()
10412 });
10413 cx.assert_editor_state(indoc! {"
10414 one.second_completionˇ
10415 two
10416 three
10417 "});
10418
10419 handle_resolve_completion_request(
10420 &mut cx,
10421 Some((
10422 indoc! {"
10423 one.second_completion
10424 two
10425 threeˇ
10426 "},
10427 "\nadditional edit",
10428 )),
10429 )
10430 .await;
10431 apply_additional_edits.await.unwrap();
10432 cx.assert_editor_state(indoc! {"
10433 one.second_completionˇ
10434 two
10435 three
10436 additional edit
10437 "});
10438
10439 cx.set_state(indoc! {"
10440 one.second_completion
10441 twoˇ
10442 threeˇ
10443 additional edit
10444 "});
10445 cx.simulate_keystroke(" ");
10446 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10447 cx.simulate_keystroke("s");
10448 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10449
10450 cx.assert_editor_state(indoc! {"
10451 one.second_completion
10452 two sˇ
10453 three sˇ
10454 additional edit
10455 "});
10456 //
10457 handle_completion_request(
10458 &mut cx,
10459 indoc! {"
10460 one.second_completion
10461 two s
10462 three <s|>
10463 additional edit
10464 "},
10465 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10466 )
10467 .await;
10468 cx.condition(|editor, _| editor.context_menu_visible())
10469 .await;
10470
10471 cx.simulate_keystroke("i");
10472
10473 handle_completion_request(
10474 &mut cx,
10475 indoc! {"
10476 one.second_completion
10477 two si
10478 three <si|>
10479 additional edit
10480 "},
10481 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10482 )
10483 .await;
10484 cx.condition(|editor, _| editor.context_menu_visible())
10485 .await;
10486
10487 let apply_additional_edits = cx.update_editor(|editor, cx| {
10488 editor
10489 .confirm_completion(&ConfirmCompletion::default(), cx)
10490 .unwrap()
10491 });
10492 cx.assert_editor_state(indoc! {"
10493 one.second_completion
10494 two sixth_completionˇ
10495 three sixth_completionˇ
10496 additional edit
10497 "});
10498
10499 handle_resolve_completion_request(&mut cx, None).await;
10500 apply_additional_edits.await.unwrap();
10501
10502 cx.update(|cx| {
10503 cx.update_global::<Settings, _, _>(|settings, _| {
10504 settings.show_completions_on_input = false;
10505 })
10506 });
10507 cx.set_state("editorˇ");
10508 cx.simulate_keystroke(".");
10509 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10510 cx.simulate_keystroke("c");
10511 cx.simulate_keystroke("l");
10512 cx.simulate_keystroke("o");
10513 cx.assert_editor_state("editor.cloˇ");
10514 assert!(cx.editor(|e, _| e.context_menu.is_none()));
10515 cx.update_editor(|editor, cx| {
10516 editor.show_completions(&ShowCompletions, cx);
10517 });
10518 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
10519 cx.condition(|editor, _| editor.context_menu_visible())
10520 .await;
10521 let apply_additional_edits = cx.update_editor(|editor, cx| {
10522 editor
10523 .confirm_completion(&ConfirmCompletion::default(), cx)
10524 .unwrap()
10525 });
10526 cx.assert_editor_state("editor.closeˇ");
10527 handle_resolve_completion_request(&mut cx, None).await;
10528 apply_additional_edits.await.unwrap();
10529
10530 // Handle completion request passing a marked string specifying where the completion
10531 // should be triggered from using '|' character, what range should be replaced, and what completions
10532 // should be returned using '<' and '>' to delimit the range
10533 async fn handle_completion_request<'a>(
10534 cx: &mut EditorLspTestContext<'a>,
10535 marked_string: &str,
10536 completions: Vec<&'static str>,
10537 ) {
10538 let complete_from_marker: TextRangeMarker = '|'.into();
10539 let replace_range_marker: TextRangeMarker = ('<', '>').into();
10540 let (_, mut marked_ranges) = marked_text_ranges_by(
10541 marked_string,
10542 vec![complete_from_marker.clone(), replace_range_marker.clone()],
10543 );
10544
10545 let complete_from_position =
10546 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
10547 let replace_range =
10548 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
10549
10550 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
10551 let completions = completions.clone();
10552 async move {
10553 assert_eq!(params.text_document_position.text_document.uri, url.clone());
10554 assert_eq!(
10555 params.text_document_position.position,
10556 complete_from_position
10557 );
10558 Ok(Some(lsp::CompletionResponse::Array(
10559 completions
10560 .iter()
10561 .map(|completion_text| lsp::CompletionItem {
10562 label: completion_text.to_string(),
10563 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10564 range: replace_range,
10565 new_text: completion_text.to_string(),
10566 })),
10567 ..Default::default()
10568 })
10569 .collect(),
10570 )))
10571 }
10572 })
10573 .next()
10574 .await;
10575 }
10576
10577 async fn handle_resolve_completion_request<'a>(
10578 cx: &mut EditorLspTestContext<'a>,
10579 edit: Option<(&'static str, &'static str)>,
10580 ) {
10581 let edit = edit.map(|(marked_string, new_text)| {
10582 let (_, marked_ranges) = marked_text_ranges(marked_string, false);
10583 let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
10584 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
10585 });
10586
10587 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
10588 let edit = edit.clone();
10589 async move {
10590 Ok(lsp::CompletionItem {
10591 additional_text_edits: edit,
10592 ..Default::default()
10593 })
10594 }
10595 })
10596 .next()
10597 .await;
10598 }
10599 }
10600
10601 #[gpui::test]
10602 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
10603 cx.update(|cx| cx.set_global(Settings::test(cx)));
10604 let language = Arc::new(Language::new(
10605 LanguageConfig {
10606 line_comment: Some("// ".to_string()),
10607 ..Default::default()
10608 },
10609 Some(tree_sitter_rust::language()),
10610 ));
10611
10612 let text = "
10613 fn a() {
10614 //b();
10615 // c();
10616 // d();
10617 }
10618 "
10619 .unindent();
10620
10621 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10622 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10623 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10624
10625 view.update(cx, |editor, cx| {
10626 // If multiple selections intersect a line, the line is only
10627 // toggled once.
10628 editor.change_selections(None, cx, |s| {
10629 s.select_display_ranges([
10630 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
10631 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
10632 ])
10633 });
10634 editor.toggle_comments(&ToggleComments, cx);
10635 assert_eq!(
10636 editor.text(cx),
10637 "
10638 fn a() {
10639 b();
10640 c();
10641 d();
10642 }
10643 "
10644 .unindent()
10645 );
10646
10647 // The comment prefix is inserted at the same column for every line
10648 // in a selection.
10649 editor.change_selections(None, cx, |s| {
10650 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
10651 });
10652 editor.toggle_comments(&ToggleComments, cx);
10653 assert_eq!(
10654 editor.text(cx),
10655 "
10656 fn a() {
10657 // b();
10658 // c();
10659 // d();
10660 }
10661 "
10662 .unindent()
10663 );
10664
10665 // If a selection ends at the beginning of a line, that line is not toggled.
10666 editor.change_selections(None, cx, |s| {
10667 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
10668 });
10669 editor.toggle_comments(&ToggleComments, cx);
10670 assert_eq!(
10671 editor.text(cx),
10672 "
10673 fn a() {
10674 // b();
10675 c();
10676 // d();
10677 }
10678 "
10679 .unindent()
10680 );
10681 });
10682 }
10683
10684 #[gpui::test]
10685 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
10686 cx.set_global(Settings::test(cx));
10687 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10688 let multibuffer = cx.add_model(|cx| {
10689 let mut multibuffer = MultiBuffer::new(0);
10690 multibuffer.push_excerpts(
10691 buffer.clone(),
10692 [
10693 ExcerptRange {
10694 context: Point::new(0, 0)..Point::new(0, 4),
10695 primary: None,
10696 },
10697 ExcerptRange {
10698 context: Point::new(1, 0)..Point::new(1, 4),
10699 primary: None,
10700 },
10701 ],
10702 cx,
10703 );
10704 multibuffer
10705 });
10706
10707 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
10708
10709 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10710 view.update(cx, |view, cx| {
10711 assert_eq!(view.text(cx), "aaaa\nbbbb");
10712 view.change_selections(None, cx, |s| {
10713 s.select_ranges([
10714 Point::new(0, 0)..Point::new(0, 0),
10715 Point::new(1, 0)..Point::new(1, 0),
10716 ])
10717 });
10718
10719 view.handle_input("X", cx);
10720 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
10721 assert_eq!(
10722 view.selections.ranges(cx),
10723 [
10724 Point::new(0, 1)..Point::new(0, 1),
10725 Point::new(1, 1)..Point::new(1, 1),
10726 ]
10727 )
10728 });
10729 }
10730
10731 #[gpui::test]
10732 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10733 cx.set_global(Settings::test(cx));
10734 let markers = vec![('[', ']').into(), ('(', ')').into()];
10735 let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10736 indoc! {"
10737 [aaaa
10738 (bbbb]
10739 cccc)",
10740 },
10741 markers.clone(),
10742 );
10743 let excerpt_ranges = markers.into_iter().map(|marker| {
10744 let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10745 ExcerptRange {
10746 context,
10747 primary: None,
10748 }
10749 });
10750 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10751 let multibuffer = cx.add_model(|cx| {
10752 let mut multibuffer = MultiBuffer::new(0);
10753 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10754 multibuffer
10755 });
10756
10757 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10758 view.update(cx, |view, cx| {
10759 let (expected_text, selection_ranges) = marked_text_ranges(
10760 indoc! {"
10761 aaaa
10762 bˇbbb
10763 bˇbbˇb
10764 cccc"
10765 },
10766 true,
10767 );
10768 assert_eq!(view.text(cx), expected_text);
10769 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10770
10771 view.handle_input("X", cx);
10772
10773 let (expected_text, expected_selections) = marked_text_ranges(
10774 indoc! {"
10775 aaaa
10776 bXˇbbXb
10777 bXˇbbXˇb
10778 cccc"
10779 },
10780 false,
10781 );
10782 assert_eq!(view.text(cx), expected_text);
10783 assert_eq!(view.selections.ranges(cx), expected_selections);
10784
10785 view.newline(&Newline, cx);
10786 let (expected_text, expected_selections) = marked_text_ranges(
10787 indoc! {"
10788 aaaa
10789 bX
10790 ˇbbX
10791 b
10792 bX
10793 ˇbbX
10794 ˇb
10795 cccc"
10796 },
10797 false,
10798 );
10799 assert_eq!(view.text(cx), expected_text);
10800 assert_eq!(view.selections.ranges(cx), expected_selections);
10801 });
10802 }
10803
10804 #[gpui::test]
10805 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10806 cx.set_global(Settings::test(cx));
10807 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10808 let mut excerpt1_id = None;
10809 let multibuffer = cx.add_model(|cx| {
10810 let mut multibuffer = MultiBuffer::new(0);
10811 excerpt1_id = multibuffer
10812 .push_excerpts(
10813 buffer.clone(),
10814 [
10815 ExcerptRange {
10816 context: Point::new(0, 0)..Point::new(1, 4),
10817 primary: None,
10818 },
10819 ExcerptRange {
10820 context: Point::new(1, 0)..Point::new(2, 4),
10821 primary: None,
10822 },
10823 ],
10824 cx,
10825 )
10826 .into_iter()
10827 .next();
10828 multibuffer
10829 });
10830 assert_eq!(
10831 multibuffer.read(cx).read(cx).text(),
10832 "aaaa\nbbbb\nbbbb\ncccc"
10833 );
10834 let (_, editor) = cx.add_window(Default::default(), |cx| {
10835 let mut editor = build_editor(multibuffer.clone(), cx);
10836 let snapshot = editor.snapshot(cx);
10837 editor.change_selections(None, cx, |s| {
10838 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10839 });
10840 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10841 assert_eq!(
10842 editor.selections.ranges(cx),
10843 [
10844 Point::new(1, 3)..Point::new(1, 3),
10845 Point::new(2, 1)..Point::new(2, 1),
10846 ]
10847 );
10848 editor
10849 });
10850
10851 // Refreshing selections is a no-op when excerpts haven't changed.
10852 editor.update(cx, |editor, cx| {
10853 editor.change_selections(None, cx, |s| {
10854 s.refresh();
10855 });
10856 assert_eq!(
10857 editor.selections.ranges(cx),
10858 [
10859 Point::new(1, 3)..Point::new(1, 3),
10860 Point::new(2, 1)..Point::new(2, 1),
10861 ]
10862 );
10863 });
10864
10865 multibuffer.update(cx, |multibuffer, cx| {
10866 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10867 });
10868 editor.update(cx, |editor, cx| {
10869 // Removing an excerpt causes the first selection to become degenerate.
10870 assert_eq!(
10871 editor.selections.ranges(cx),
10872 [
10873 Point::new(0, 0)..Point::new(0, 0),
10874 Point::new(0, 1)..Point::new(0, 1)
10875 ]
10876 );
10877
10878 // Refreshing selections will relocate the first selection to the original buffer
10879 // location.
10880 editor.change_selections(None, cx, |s| {
10881 s.refresh();
10882 });
10883 assert_eq!(
10884 editor.selections.ranges(cx),
10885 [
10886 Point::new(0, 1)..Point::new(0, 1),
10887 Point::new(0, 3)..Point::new(0, 3)
10888 ]
10889 );
10890 assert!(editor.selections.pending_anchor().is_some());
10891 });
10892 }
10893
10894 #[gpui::test]
10895 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10896 cx.set_global(Settings::test(cx));
10897 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10898 let mut excerpt1_id = None;
10899 let multibuffer = cx.add_model(|cx| {
10900 let mut multibuffer = MultiBuffer::new(0);
10901 excerpt1_id = multibuffer
10902 .push_excerpts(
10903 buffer.clone(),
10904 [
10905 ExcerptRange {
10906 context: Point::new(0, 0)..Point::new(1, 4),
10907 primary: None,
10908 },
10909 ExcerptRange {
10910 context: Point::new(1, 0)..Point::new(2, 4),
10911 primary: None,
10912 },
10913 ],
10914 cx,
10915 )
10916 .into_iter()
10917 .next();
10918 multibuffer
10919 });
10920 assert_eq!(
10921 multibuffer.read(cx).read(cx).text(),
10922 "aaaa\nbbbb\nbbbb\ncccc"
10923 );
10924 let (_, editor) = cx.add_window(Default::default(), |cx| {
10925 let mut editor = build_editor(multibuffer.clone(), cx);
10926 let snapshot = editor.snapshot(cx);
10927 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10928 assert_eq!(
10929 editor.selections.ranges(cx),
10930 [Point::new(1, 3)..Point::new(1, 3)]
10931 );
10932 editor
10933 });
10934
10935 multibuffer.update(cx, |multibuffer, cx| {
10936 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10937 });
10938 editor.update(cx, |editor, cx| {
10939 assert_eq!(
10940 editor.selections.ranges(cx),
10941 [Point::new(0, 0)..Point::new(0, 0)]
10942 );
10943
10944 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10945 editor.change_selections(None, cx, |s| {
10946 s.refresh();
10947 });
10948 assert_eq!(
10949 editor.selections.ranges(cx),
10950 [Point::new(0, 3)..Point::new(0, 3)]
10951 );
10952 assert!(editor.selections.pending_anchor().is_some());
10953 });
10954 }
10955
10956 #[gpui::test]
10957 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10958 cx.update(|cx| cx.set_global(Settings::test(cx)));
10959 let language = Arc::new(
10960 Language::new(
10961 LanguageConfig {
10962 brackets: vec![
10963 BracketPair {
10964 start: "{".to_string(),
10965 end: "}".to_string(),
10966 close: true,
10967 newline: true,
10968 },
10969 BracketPair {
10970 start: "/* ".to_string(),
10971 end: " */".to_string(),
10972 close: true,
10973 newline: true,
10974 },
10975 ],
10976 ..Default::default()
10977 },
10978 Some(tree_sitter_rust::language()),
10979 )
10980 .with_indents_query("")
10981 .unwrap(),
10982 );
10983
10984 let text = concat!(
10985 "{ }\n", // Suppress rustfmt
10986 " x\n", //
10987 " /* */\n", //
10988 "x\n", //
10989 "{{} }\n", //
10990 );
10991
10992 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10993 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10994 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10995 view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10996 .await;
10997
10998 view.update(cx, |view, cx| {
10999 view.change_selections(None, cx, |s| {
11000 s.select_display_ranges([
11001 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
11002 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
11003 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
11004 ])
11005 });
11006 view.newline(&Newline, cx);
11007
11008 assert_eq!(
11009 view.buffer().read(cx).read(cx).text(),
11010 concat!(
11011 "{ \n", // Suppress rustfmt
11012 "\n", //
11013 "}\n", //
11014 " x\n", //
11015 " /* \n", //
11016 " \n", //
11017 " */\n", //
11018 "x\n", //
11019 "{{} \n", //
11020 "}\n", //
11021 )
11022 );
11023 });
11024 }
11025
11026 #[gpui::test]
11027 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
11028 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11029
11030 cx.set_global(Settings::test(cx));
11031 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11032
11033 editor.update(cx, |editor, cx| {
11034 struct Type1;
11035 struct Type2;
11036
11037 let buffer = buffer.read(cx).snapshot(cx);
11038
11039 let anchor_range = |range: Range<Point>| {
11040 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
11041 };
11042
11043 editor.highlight_background::<Type1>(
11044 vec![
11045 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11046 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11047 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11048 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11049 ],
11050 |_| Color::red(),
11051 cx,
11052 );
11053 editor.highlight_background::<Type2>(
11054 vec![
11055 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11056 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11057 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11058 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11059 ],
11060 |_| Color::green(),
11061 cx,
11062 );
11063
11064 let snapshot = editor.snapshot(cx);
11065 let mut highlighted_ranges = editor.background_highlights_in_range(
11066 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11067 &snapshot,
11068 cx.global::<Settings>().theme.as_ref(),
11069 );
11070 // Enforce a consistent ordering based on color without relying on the ordering of the
11071 // highlight's `TypeId` which is non-deterministic.
11072 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11073 assert_eq!(
11074 highlighted_ranges,
11075 &[
11076 (
11077 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
11078 Color::green(),
11079 ),
11080 (
11081 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
11082 Color::green(),
11083 ),
11084 (
11085 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
11086 Color::red(),
11087 ),
11088 (
11089 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11090 Color::red(),
11091 ),
11092 ]
11093 );
11094 assert_eq!(
11095 editor.background_highlights_in_range(
11096 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11097 &snapshot,
11098 cx.global::<Settings>().theme.as_ref(),
11099 ),
11100 &[(
11101 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
11102 Color::red(),
11103 )]
11104 );
11105 });
11106 }
11107
11108 #[gpui::test]
11109 fn test_following(cx: &mut gpui::MutableAppContext) {
11110 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11111
11112 cx.set_global(Settings::test(cx));
11113
11114 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
11115 let (_, follower) = cx.add_window(
11116 WindowOptions {
11117 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
11118 ..Default::default()
11119 },
11120 |cx| build_editor(buffer.clone(), cx),
11121 );
11122
11123 let pending_update = Rc::new(RefCell::new(None));
11124 follower.update(cx, {
11125 let update = pending_update.clone();
11126 |_, cx| {
11127 cx.subscribe(&leader, move |_, leader, event, cx| {
11128 leader
11129 .read(cx)
11130 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
11131 })
11132 .detach();
11133 }
11134 });
11135
11136 // Update the selections only
11137 leader.update(cx, |leader, cx| {
11138 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11139 });
11140 follower.update(cx, |follower, cx| {
11141 follower
11142 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11143 .unwrap();
11144 });
11145 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
11146
11147 // Update the scroll position only
11148 leader.update(cx, |leader, cx| {
11149 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11150 });
11151 follower.update(cx, |follower, cx| {
11152 follower
11153 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11154 .unwrap();
11155 });
11156 assert_eq!(
11157 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
11158 vec2f(1.5, 3.5)
11159 );
11160
11161 // Update the selections and scroll position
11162 leader.update(cx, |leader, cx| {
11163 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
11164 leader.request_autoscroll(Autoscroll::Newest, cx);
11165 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
11166 });
11167 follower.update(cx, |follower, cx| {
11168 let initial_scroll_position = follower.scroll_position(cx);
11169 follower
11170 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11171 .unwrap();
11172 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
11173 assert!(follower.autoscroll_request.is_some());
11174 });
11175 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
11176
11177 // Creating a pending selection that precedes another selection
11178 leader.update(cx, |leader, cx| {
11179 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
11180 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
11181 });
11182 follower.update(cx, |follower, cx| {
11183 follower
11184 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11185 .unwrap();
11186 });
11187 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
11188
11189 // Extend the pending selection so that it surrounds another selection
11190 leader.update(cx, |leader, cx| {
11191 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
11192 });
11193 follower.update(cx, |follower, cx| {
11194 follower
11195 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
11196 .unwrap();
11197 });
11198 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
11199 }
11200
11201 #[test]
11202 fn test_combine_syntax_and_fuzzy_match_highlights() {
11203 let string = "abcdefghijklmnop";
11204 let syntax_ranges = [
11205 (
11206 0..3,
11207 HighlightStyle {
11208 color: Some(Color::red()),
11209 ..Default::default()
11210 },
11211 ),
11212 (
11213 4..8,
11214 HighlightStyle {
11215 color: Some(Color::green()),
11216 ..Default::default()
11217 },
11218 ),
11219 ];
11220 let match_indices = [4, 6, 7, 8];
11221 assert_eq!(
11222 combine_syntax_and_fuzzy_match_highlights(
11223 string,
11224 Default::default(),
11225 syntax_ranges.into_iter(),
11226 &match_indices,
11227 ),
11228 &[
11229 (
11230 0..3,
11231 HighlightStyle {
11232 color: Some(Color::red()),
11233 ..Default::default()
11234 },
11235 ),
11236 (
11237 4..5,
11238 HighlightStyle {
11239 color: Some(Color::green()),
11240 weight: Some(fonts::Weight::BOLD),
11241 ..Default::default()
11242 },
11243 ),
11244 (
11245 5..6,
11246 HighlightStyle {
11247 color: Some(Color::green()),
11248 ..Default::default()
11249 },
11250 ),
11251 (
11252 6..8,
11253 HighlightStyle {
11254 color: Some(Color::green()),
11255 weight: Some(fonts::Weight::BOLD),
11256 ..Default::default()
11257 },
11258 ),
11259 (
11260 8..9,
11261 HighlightStyle {
11262 weight: Some(fonts::Weight::BOLD),
11263 ..Default::default()
11264 },
11265 ),
11266 ]
11267 );
11268 }
11269
11270 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
11271 let point = DisplayPoint::new(row as u32, column as u32);
11272 point..point
11273 }
11274
11275 fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
11276 let (text, ranges) = marked_text_ranges(marked_text, true);
11277 assert_eq!(view.text(cx), text);
11278 assert_eq!(
11279 view.selections.ranges(cx),
11280 ranges,
11281 "Assert selections are {}",
11282 marked_text
11283 );
11284 }
11285}
11286
11287trait RangeExt<T> {
11288 fn sorted(&self) -> Range<T>;
11289 fn to_inclusive(&self) -> RangeInclusive<T>;
11290}
11291
11292impl<T: Ord + Clone> RangeExt<T> for Range<T> {
11293 fn sorted(&self) -> Self {
11294 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
11295 }
11296
11297 fn to_inclusive(&self) -> RangeInclusive<T> {
11298 self.start.clone()..=self.end.clone()
11299 }
11300}
11301
11302trait RangeToAnchorExt {
11303 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11304}
11305
11306impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11307 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11308 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11309 }
11310}