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