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