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 = cx
5595 .background()
5596 .timer(Duration::from_millis(milliseconds))
5597 .fuse();
5598 pending_autosave.await;
5599 futures::select_biased! {
5600 _ = cancel_rx => return None,
5601 _ = timer => {}
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)
5638 && !self.buffer.read(cx).has_conflict(cx)
5639 && workspace::Item::can_save(self, cx)
5640 {
5641 return workspace::Item::save(self, project, cx);
5642 }
5643 }
5644
5645 Task::ready(Ok(()))
5646 }
5647
5648 pub fn set_searchable(&mut self, searchable: bool) {
5649 self.searchable = searchable;
5650 }
5651
5652 pub fn searchable(&self) -> bool {
5653 self.searchable
5654 }
5655
5656 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5657 let active_item = workspace.active_item(cx);
5658 let editor_handle = if let Some(editor) = active_item
5659 .as_ref()
5660 .and_then(|item| item.act_as::<Self>(cx))
5661 {
5662 editor
5663 } else {
5664 cx.propagate_action();
5665 return;
5666 };
5667
5668 let editor = editor_handle.read(cx);
5669 let buffer = editor.buffer.read(cx);
5670 if buffer.is_singleton() {
5671 cx.propagate_action();
5672 return;
5673 }
5674
5675 let mut new_selections_by_buffer = HashMap::default();
5676 for selection in editor.selections.all::<usize>(cx) {
5677 for (buffer, mut range) in
5678 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5679 {
5680 if selection.reversed {
5681 mem::swap(&mut range.start, &mut range.end);
5682 }
5683 new_selections_by_buffer
5684 .entry(buffer)
5685 .or_insert(Vec::new())
5686 .push(range)
5687 }
5688 }
5689
5690 editor_handle.update(cx, |editor, cx| {
5691 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5692 });
5693 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5694 nav_history.borrow_mut().disable();
5695
5696 // We defer the pane interaction because we ourselves are a workspace item
5697 // and activating a new item causes the pane to call a method on us reentrantly,
5698 // which panics if we're on the stack.
5699 cx.defer(move |workspace, cx| {
5700 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5701 let editor = workspace.open_project_item::<Self>(buffer, cx);
5702 editor.update(cx, |editor, cx| {
5703 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5704 s.select_ranges(ranges);
5705 });
5706 });
5707 }
5708
5709 nav_history.borrow_mut().enable();
5710 });
5711 }
5712
5713 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5714 let editor = workspace.open_path(action.path.clone(), true, cx);
5715 let position = action.position;
5716 let anchor = action.anchor;
5717 cx.spawn_weak(|_, mut cx| async move {
5718 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5719 editor.update(&mut cx, |editor, cx| {
5720 let buffer = editor.buffer().read(cx).as_singleton()?;
5721 let buffer = buffer.read(cx);
5722 let cursor = if buffer.can_resolve(&anchor) {
5723 language::ToPoint::to_point(&anchor, buffer)
5724 } else {
5725 buffer.clip_point(position, Bias::Left)
5726 };
5727
5728 let nav_history = editor.nav_history.take();
5729 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5730 s.select_ranges([cursor..cursor]);
5731 });
5732 editor.nav_history = nav_history;
5733
5734 Some(())
5735 })?;
5736 Some(())
5737 })
5738 .detach()
5739 }
5740}
5741
5742impl EditorSnapshot {
5743 pub fn is_focused(&self) -> bool {
5744 self.is_focused
5745 }
5746
5747 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5748 self.placeholder_text.as_ref()
5749 }
5750
5751 pub fn scroll_position(&self) -> Vector2F {
5752 compute_scroll_position(
5753 &self.display_snapshot,
5754 self.scroll_position,
5755 &self.scroll_top_anchor,
5756 )
5757 }
5758}
5759
5760impl Deref for EditorSnapshot {
5761 type Target = DisplaySnapshot;
5762
5763 fn deref(&self) -> &Self::Target {
5764 &self.display_snapshot
5765 }
5766}
5767
5768fn compute_scroll_position(
5769 snapshot: &DisplaySnapshot,
5770 mut scroll_position: Vector2F,
5771 scroll_top_anchor: &Anchor,
5772) -> Vector2F {
5773 if *scroll_top_anchor != Anchor::min() {
5774 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5775 scroll_position.set_y(scroll_top + scroll_position.y());
5776 } else {
5777 scroll_position.set_y(0.);
5778 }
5779 scroll_position
5780}
5781
5782#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5783pub enum Event {
5784 Activate,
5785 BufferEdited,
5786 Edited,
5787 Reparsed,
5788 Blurred,
5789 DirtyChanged,
5790 Saved,
5791 TitleChanged,
5792 SelectionsChanged { local: bool },
5793 ScrollPositionChanged { local: bool },
5794 Closed,
5795}
5796
5797pub struct EditorFocused(pub ViewHandle<Editor>);
5798pub struct EditorBlurred(pub ViewHandle<Editor>);
5799pub struct EditorReleased(pub WeakViewHandle<Editor>);
5800
5801impl Entity for Editor {
5802 type Event = Event;
5803
5804 fn release(&mut self, cx: &mut MutableAppContext) {
5805 cx.emit_global(EditorReleased(self.handle.clone()));
5806 }
5807}
5808
5809impl View for Editor {
5810 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5811 let style = self.style(cx);
5812 let font_changed = self.display_map.update(cx, |map, cx| {
5813 map.set_font(style.text.font_id, style.text.font_size, cx)
5814 });
5815
5816 if font_changed {
5817 let handle = self.handle.clone();
5818 cx.defer(move |cx| {
5819 if let Some(editor) = handle.upgrade(cx) {
5820 editor.update(cx, |editor, cx| {
5821 hide_hover(editor, cx);
5822 })
5823 }
5824 });
5825 }
5826
5827 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5828 }
5829
5830 fn ui_name() -> &'static str {
5831 "Editor"
5832 }
5833
5834 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5835 let focused_event = EditorFocused(cx.handle());
5836 cx.emit_global(focused_event);
5837 if let Some(rename) = self.pending_rename.as_ref() {
5838 cx.focus(&rename.editor);
5839 } else {
5840 self.focused = true;
5841 self.blink_cursors(self.blink_epoch, cx);
5842 self.buffer.update(cx, |buffer, cx| {
5843 buffer.finalize_last_transaction(cx);
5844 if self.leader_replica_id.is_none() {
5845 buffer.set_active_selections(
5846 &self.selections.disjoint_anchors(),
5847 self.selections.line_mode,
5848 cx,
5849 );
5850 }
5851 });
5852 }
5853 }
5854
5855 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5856 let blurred_event = EditorBlurred(cx.handle());
5857 cx.emit_global(blurred_event);
5858 self.focused = false;
5859 self.buffer
5860 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5861 self.hide_context_menu(cx);
5862 hide_hover(self, cx);
5863 cx.emit(Event::Blurred);
5864 cx.notify();
5865
5866 if cx.global::<Settings>().autosave == Autosave::OnFocusChange {
5867 self.autosave(cx).detach_and_log_err(cx);
5868 }
5869 }
5870
5871 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5872 let mut context = Self::default_keymap_context();
5873 let mode = match self.mode {
5874 EditorMode::SingleLine => "single_line",
5875 EditorMode::AutoHeight { .. } => "auto_height",
5876 EditorMode::Full => "full",
5877 };
5878 context.map.insert("mode".into(), mode.into());
5879 if self.pending_rename.is_some() {
5880 context.set.insert("renaming".into());
5881 }
5882 match self.context_menu.as_ref() {
5883 Some(ContextMenu::Completions(_)) => {
5884 context.set.insert("showing_completions".into());
5885 }
5886 Some(ContextMenu::CodeActions(_)) => {
5887 context.set.insert("showing_code_actions".into());
5888 }
5889 None => {}
5890 }
5891
5892 for layer in self.keymap_context_layers.values() {
5893 context.extend(layer);
5894 }
5895
5896 context
5897 }
5898}
5899
5900fn build_style(
5901 settings: &Settings,
5902 get_field_editor_theme: Option<GetFieldEditorTheme>,
5903 override_text_style: Option<&OverrideTextStyle>,
5904 cx: &AppContext,
5905) -> EditorStyle {
5906 let font_cache = cx.font_cache();
5907
5908 let mut theme = settings.theme.editor.clone();
5909 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5910 let field_editor_theme = get_field_editor_theme(&settings.theme);
5911 theme.text_color = field_editor_theme.text.color;
5912 theme.selection = field_editor_theme.selection;
5913 theme.background = field_editor_theme
5914 .container
5915 .background_color
5916 .unwrap_or_default();
5917 EditorStyle {
5918 text: field_editor_theme.text,
5919 placeholder_text: field_editor_theme.placeholder_text,
5920 theme,
5921 }
5922 } else {
5923 let font_family_id = settings.buffer_font_family;
5924 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5925 let font_properties = Default::default();
5926 let font_id = font_cache
5927 .select_font(font_family_id, &font_properties)
5928 .unwrap();
5929 let font_size = settings.buffer_font_size;
5930 EditorStyle {
5931 text: TextStyle {
5932 color: settings.theme.editor.text_color,
5933 font_family_name,
5934 font_family_id,
5935 font_id,
5936 font_size,
5937 font_properties,
5938 underline: Default::default(),
5939 },
5940 placeholder_text: None,
5941 theme,
5942 }
5943 };
5944
5945 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5946 if let Some(highlighted) = style
5947 .text
5948 .clone()
5949 .highlight(highlight_style, font_cache)
5950 .log_err()
5951 {
5952 style.text = highlighted;
5953 }
5954 }
5955
5956 style
5957}
5958
5959trait SelectionExt {
5960 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5961 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5962 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5963 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5964 -> Range<u32>;
5965}
5966
5967impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5968 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5969 let start = self.start.to_point(buffer);
5970 let end = self.end.to_point(buffer);
5971 if self.reversed {
5972 end..start
5973 } else {
5974 start..end
5975 }
5976 }
5977
5978 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5979 let start = self.start.to_offset(buffer);
5980 let end = self.end.to_offset(buffer);
5981 if self.reversed {
5982 end..start
5983 } else {
5984 start..end
5985 }
5986 }
5987
5988 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5989 let start = self
5990 .start
5991 .to_point(&map.buffer_snapshot)
5992 .to_display_point(map);
5993 let end = self
5994 .end
5995 .to_point(&map.buffer_snapshot)
5996 .to_display_point(map);
5997 if self.reversed {
5998 end..start
5999 } else {
6000 start..end
6001 }
6002 }
6003
6004 fn spanned_rows(
6005 &self,
6006 include_end_if_at_line_start: bool,
6007 map: &DisplaySnapshot,
6008 ) -> Range<u32> {
6009 let start = self.start.to_point(&map.buffer_snapshot);
6010 let mut end = self.end.to_point(&map.buffer_snapshot);
6011 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6012 end.row -= 1;
6013 }
6014
6015 let buffer_start = map.prev_line_boundary(start).0;
6016 let buffer_end = map.next_line_boundary(end).0;
6017 buffer_start.row..buffer_end.row + 1
6018 }
6019}
6020
6021impl<T: InvalidationRegion> InvalidationStack<T> {
6022 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6023 where
6024 S: Clone + ToOffset,
6025 {
6026 while let Some(region) = self.last() {
6027 let all_selections_inside_invalidation_ranges =
6028 if selections.len() == region.ranges().len() {
6029 selections
6030 .iter()
6031 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6032 .all(|(selection, invalidation_range)| {
6033 let head = selection.head().to_offset(&buffer);
6034 invalidation_range.start <= head && invalidation_range.end >= head
6035 })
6036 } else {
6037 false
6038 };
6039
6040 if all_selections_inside_invalidation_ranges {
6041 break;
6042 } else {
6043 self.pop();
6044 }
6045 }
6046 }
6047}
6048
6049impl<T> Default for InvalidationStack<T> {
6050 fn default() -> Self {
6051 Self(Default::default())
6052 }
6053}
6054
6055impl<T> Deref for InvalidationStack<T> {
6056 type Target = Vec<T>;
6057
6058 fn deref(&self) -> &Self::Target {
6059 &self.0
6060 }
6061}
6062
6063impl<T> DerefMut for InvalidationStack<T> {
6064 fn deref_mut(&mut self) -> &mut Self::Target {
6065 &mut self.0
6066 }
6067}
6068
6069impl InvalidationRegion for BracketPairState {
6070 fn ranges(&self) -> &[Range<Anchor>] {
6071 &self.ranges
6072 }
6073}
6074
6075impl InvalidationRegion for SnippetState {
6076 fn ranges(&self) -> &[Range<Anchor>] {
6077 &self.ranges[self.active_index]
6078 }
6079}
6080
6081impl Deref for EditorStyle {
6082 type Target = theme::Editor;
6083
6084 fn deref(&self) -> &Self::Target {
6085 &self.theme
6086 }
6087}
6088
6089pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6090 let mut highlighted_lines = Vec::new();
6091 for line in diagnostic.message.lines() {
6092 highlighted_lines.push(highlight_diagnostic_message(line));
6093 }
6094
6095 Arc::new(move |cx: &mut BlockContext| {
6096 let settings = cx.global::<Settings>();
6097 let theme = &settings.theme.editor;
6098 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6099 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6100 Flex::column()
6101 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6102 Label::new(
6103 line.clone(),
6104 style.message.clone().with_font_size(font_size),
6105 )
6106 .with_highlights(highlights.clone())
6107 .contained()
6108 .with_margin_left(cx.anchor_x)
6109 .boxed()
6110 }))
6111 .aligned()
6112 .left()
6113 .boxed()
6114 })
6115}
6116
6117pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6118 let mut message_without_backticks = String::new();
6119 let mut prev_offset = 0;
6120 let mut inside_block = false;
6121 let mut highlights = Vec::new();
6122 for (match_ix, (offset, _)) in message
6123 .match_indices('`')
6124 .chain([(message.len(), "")])
6125 .enumerate()
6126 {
6127 message_without_backticks.push_str(&message[prev_offset..offset]);
6128 if inside_block {
6129 highlights.extend(prev_offset - match_ix..offset - match_ix);
6130 }
6131
6132 inside_block = !inside_block;
6133 prev_offset = offset + 1;
6134 }
6135
6136 (message_without_backticks, highlights)
6137}
6138
6139pub fn diagnostic_style(
6140 severity: DiagnosticSeverity,
6141 valid: bool,
6142 theme: &theme::Editor,
6143) -> DiagnosticStyle {
6144 match (severity, valid) {
6145 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6146 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6147 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6148 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6149 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6150 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6151 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6152 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6153 _ => theme.invalid_hint_diagnostic.clone(),
6154 }
6155}
6156
6157pub fn combine_syntax_and_fuzzy_match_highlights(
6158 text: &str,
6159 default_style: HighlightStyle,
6160 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6161 match_indices: &[usize],
6162) -> Vec<(Range<usize>, HighlightStyle)> {
6163 let mut result = Vec::new();
6164 let mut match_indices = match_indices.iter().copied().peekable();
6165
6166 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6167 {
6168 syntax_highlight.weight = None;
6169
6170 // Add highlights for any fuzzy match characters before the next
6171 // syntax highlight range.
6172 while let Some(&match_index) = match_indices.peek() {
6173 if match_index >= range.start {
6174 break;
6175 }
6176 match_indices.next();
6177 let end_index = char_ix_after(match_index, text);
6178 let mut match_style = default_style;
6179 match_style.weight = Some(fonts::Weight::BOLD);
6180 result.push((match_index..end_index, match_style));
6181 }
6182
6183 if range.start == usize::MAX {
6184 break;
6185 }
6186
6187 // Add highlights for any fuzzy match characters within the
6188 // syntax highlight range.
6189 let mut offset = range.start;
6190 while let Some(&match_index) = match_indices.peek() {
6191 if match_index >= range.end {
6192 break;
6193 }
6194
6195 match_indices.next();
6196 if match_index > offset {
6197 result.push((offset..match_index, syntax_highlight));
6198 }
6199
6200 let mut end_index = char_ix_after(match_index, text);
6201 while let Some(&next_match_index) = match_indices.peek() {
6202 if next_match_index == end_index && next_match_index < range.end {
6203 end_index = char_ix_after(next_match_index, text);
6204 match_indices.next();
6205 } else {
6206 break;
6207 }
6208 }
6209
6210 let mut match_style = syntax_highlight;
6211 match_style.weight = Some(fonts::Weight::BOLD);
6212 result.push((match_index..end_index, match_style));
6213 offset = end_index;
6214 }
6215
6216 if offset < range.end {
6217 result.push((offset..range.end, syntax_highlight));
6218 }
6219 }
6220
6221 fn char_ix_after(ix: usize, text: &str) -> usize {
6222 ix + text[ix..].chars().next().unwrap().len_utf8()
6223 }
6224
6225 result
6226}
6227
6228pub fn styled_runs_for_code_label<'a>(
6229 label: &'a CodeLabel,
6230 syntax_theme: &'a theme::SyntaxTheme,
6231) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6232 let fade_out = HighlightStyle {
6233 fade_out: Some(0.35),
6234 ..Default::default()
6235 };
6236
6237 let mut prev_end = label.filter_range.end;
6238 label
6239 .runs
6240 .iter()
6241 .enumerate()
6242 .flat_map(move |(ix, (range, highlight_id))| {
6243 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6244 style
6245 } else {
6246 return Default::default();
6247 };
6248 let mut muted_style = style.clone();
6249 muted_style.highlight(fade_out);
6250
6251 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6252 if range.start >= label.filter_range.end {
6253 if range.start > prev_end {
6254 runs.push((prev_end..range.start, fade_out));
6255 }
6256 runs.push((range.clone(), muted_style));
6257 } else if range.end <= label.filter_range.end {
6258 runs.push((range.clone(), style));
6259 } else {
6260 runs.push((range.start..label.filter_range.end, style));
6261 runs.push((label.filter_range.end..range.end, muted_style));
6262 }
6263 prev_end = cmp::max(prev_end, range.end);
6264
6265 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6266 runs.push((prev_end..label.text.len(), fade_out));
6267 }
6268
6269 runs
6270 })
6271}
6272
6273#[cfg(test)]
6274mod tests {
6275 use crate::test::{
6276 assert_text_with_selections, build_editor, select_ranges, EditorTestContext,
6277 };
6278
6279 use super::*;
6280 use futures::StreamExt;
6281 use gpui::{
6282 executor::Deterministic,
6283 geometry::rect::RectF,
6284 platform::{WindowBounds, WindowOptions},
6285 };
6286 use indoc::indoc;
6287 use language::{FakeLspAdapter, LanguageConfig};
6288 use lsp::FakeLanguageServer;
6289 use project::{FakeFs, Fs};
6290 use settings::LanguageSettings;
6291 use std::{cell::RefCell, path::Path, rc::Rc, time::Instant};
6292 use text::Point;
6293 use unindent::Unindent;
6294 use util::{
6295 assert_set_eq,
6296 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6297 };
6298 use workspace::{FollowableItem, Item, ItemHandle};
6299
6300 #[gpui::test]
6301 fn test_edit_events(cx: &mut MutableAppContext) {
6302 cx.set_global(Settings::test(cx));
6303 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6304
6305 let events = Rc::new(RefCell::new(Vec::new()));
6306 let (_, editor1) = cx.add_window(Default::default(), {
6307 let events = events.clone();
6308 |cx| {
6309 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6310 if matches!(
6311 event,
6312 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6313 ) {
6314 events.borrow_mut().push(("editor1", *event));
6315 }
6316 })
6317 .detach();
6318 Editor::for_buffer(buffer.clone(), None, cx)
6319 }
6320 });
6321 let (_, editor2) = cx.add_window(Default::default(), {
6322 let events = events.clone();
6323 |cx| {
6324 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6325 if matches!(
6326 event,
6327 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6328 ) {
6329 events.borrow_mut().push(("editor2", *event));
6330 }
6331 })
6332 .detach();
6333 Editor::for_buffer(buffer.clone(), None, cx)
6334 }
6335 });
6336 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6337
6338 // Mutating editor 1 will emit an `Edited` event only for that editor.
6339 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6340 assert_eq!(
6341 mem::take(&mut *events.borrow_mut()),
6342 [
6343 ("editor1", Event::Edited),
6344 ("editor1", Event::BufferEdited),
6345 ("editor2", Event::BufferEdited),
6346 ("editor1", Event::DirtyChanged),
6347 ("editor2", Event::DirtyChanged)
6348 ]
6349 );
6350
6351 // Mutating editor 2 will emit an `Edited` event only for that editor.
6352 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6353 assert_eq!(
6354 mem::take(&mut *events.borrow_mut()),
6355 [
6356 ("editor2", Event::Edited),
6357 ("editor1", Event::BufferEdited),
6358 ("editor2", Event::BufferEdited),
6359 ]
6360 );
6361
6362 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6363 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6364 assert_eq!(
6365 mem::take(&mut *events.borrow_mut()),
6366 [
6367 ("editor1", Event::Edited),
6368 ("editor1", Event::BufferEdited),
6369 ("editor2", Event::BufferEdited),
6370 ("editor1", Event::DirtyChanged),
6371 ("editor2", Event::DirtyChanged),
6372 ]
6373 );
6374
6375 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6376 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6377 assert_eq!(
6378 mem::take(&mut *events.borrow_mut()),
6379 [
6380 ("editor1", Event::Edited),
6381 ("editor1", Event::BufferEdited),
6382 ("editor2", Event::BufferEdited),
6383 ("editor1", Event::DirtyChanged),
6384 ("editor2", Event::DirtyChanged),
6385 ]
6386 );
6387
6388 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6389 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6390 assert_eq!(
6391 mem::take(&mut *events.borrow_mut()),
6392 [
6393 ("editor2", Event::Edited),
6394 ("editor1", Event::BufferEdited),
6395 ("editor2", Event::BufferEdited),
6396 ("editor1", Event::DirtyChanged),
6397 ("editor2", Event::DirtyChanged),
6398 ]
6399 );
6400
6401 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6402 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6403 assert_eq!(
6404 mem::take(&mut *events.borrow_mut()),
6405 [
6406 ("editor2", Event::Edited),
6407 ("editor1", Event::BufferEdited),
6408 ("editor2", Event::BufferEdited),
6409 ("editor1", Event::DirtyChanged),
6410 ("editor2", Event::DirtyChanged),
6411 ]
6412 );
6413
6414 // No event is emitted when the mutation is a no-op.
6415 editor2.update(cx, |editor, cx| {
6416 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6417
6418 editor.backspace(&Backspace, cx);
6419 });
6420 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6421 }
6422
6423 #[gpui::test]
6424 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6425 cx.set_global(Settings::test(cx));
6426 let mut now = Instant::now();
6427 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6428 let group_interval = buffer.read(cx).transaction_group_interval();
6429 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6430 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6431
6432 editor.update(cx, |editor, cx| {
6433 editor.start_transaction_at(now, cx);
6434 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6435
6436 editor.insert("cd", cx);
6437 editor.end_transaction_at(now, cx);
6438 assert_eq!(editor.text(cx), "12cd56");
6439 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6440
6441 editor.start_transaction_at(now, cx);
6442 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6443 editor.insert("e", cx);
6444 editor.end_transaction_at(now, cx);
6445 assert_eq!(editor.text(cx), "12cde6");
6446 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6447
6448 now += group_interval + Duration::from_millis(1);
6449 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6450
6451 // Simulate an edit in another editor
6452 buffer.update(cx, |buffer, cx| {
6453 buffer.start_transaction_at(now, cx);
6454 buffer.edit([(0..1, "a")], cx);
6455 buffer.edit([(1..1, "b")], cx);
6456 buffer.end_transaction_at(now, cx);
6457 });
6458
6459 assert_eq!(editor.text(cx), "ab2cde6");
6460 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6461
6462 // Last transaction happened past the group interval in a different editor.
6463 // Undo it individually and don't restore selections.
6464 editor.undo(&Undo, cx);
6465 assert_eq!(editor.text(cx), "12cde6");
6466 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6467
6468 // First two transactions happened within the group interval in this editor.
6469 // Undo them together and restore selections.
6470 editor.undo(&Undo, cx);
6471 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6472 assert_eq!(editor.text(cx), "123456");
6473 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6474
6475 // Redo the first two transactions together.
6476 editor.redo(&Redo, cx);
6477 assert_eq!(editor.text(cx), "12cde6");
6478 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6479
6480 // Redo the last transaction on its own.
6481 editor.redo(&Redo, cx);
6482 assert_eq!(editor.text(cx), "ab2cde6");
6483 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6484
6485 // Test empty transactions.
6486 editor.start_transaction_at(now, cx);
6487 editor.end_transaction_at(now, cx);
6488 editor.undo(&Undo, cx);
6489 assert_eq!(editor.text(cx), "12cde6");
6490 });
6491 }
6492
6493 #[gpui::test]
6494 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6495 cx.set_global(Settings::test(cx));
6496
6497 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6498 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6499 editor.update(cx, |view, cx| {
6500 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6501 });
6502 assert_eq!(
6503 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6504 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6505 );
6506
6507 editor.update(cx, |view, cx| {
6508 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6509 });
6510
6511 assert_eq!(
6512 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6513 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6514 );
6515
6516 editor.update(cx, |view, cx| {
6517 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6518 });
6519
6520 assert_eq!(
6521 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6522 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6523 );
6524
6525 editor.update(cx, |view, cx| {
6526 view.end_selection(cx);
6527 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6528 });
6529
6530 assert_eq!(
6531 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6532 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6533 );
6534
6535 editor.update(cx, |view, cx| {
6536 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6537 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6538 });
6539
6540 assert_eq!(
6541 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6542 [
6543 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6544 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6545 ]
6546 );
6547
6548 editor.update(cx, |view, cx| {
6549 view.end_selection(cx);
6550 });
6551
6552 assert_eq!(
6553 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6554 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6555 );
6556 }
6557
6558 #[gpui::test]
6559 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6560 cx.set_global(Settings::test(cx));
6561 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6562 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6563
6564 view.update(cx, |view, cx| {
6565 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6566 assert_eq!(
6567 view.selections.display_ranges(cx),
6568 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6569 );
6570 });
6571
6572 view.update(cx, |view, cx| {
6573 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6574 assert_eq!(
6575 view.selections.display_ranges(cx),
6576 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6577 );
6578 });
6579
6580 view.update(cx, |view, cx| {
6581 view.cancel(&Cancel, cx);
6582 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6583 assert_eq!(
6584 view.selections.display_ranges(cx),
6585 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6586 );
6587 });
6588 }
6589
6590 #[gpui::test]
6591 fn test_clone(cx: &mut gpui::MutableAppContext) {
6592 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6593 one
6594 two
6595 three[]
6596 four
6597 five[]
6598 "});
6599 cx.set_global(Settings::test(cx));
6600 let buffer = MultiBuffer::build_simple(&text, cx);
6601
6602 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6603
6604 editor.update(cx, |editor, cx| {
6605 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6606 editor.fold_ranges(
6607 [
6608 Point::new(1, 0)..Point::new(2, 0),
6609 Point::new(3, 0)..Point::new(4, 0),
6610 ],
6611 cx,
6612 );
6613 });
6614
6615 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6616 cx.add_window(Default::default(), |cx| editor.clone(cx))
6617 });
6618
6619 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6620 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6621
6622 assert_eq!(
6623 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6624 editor.update(cx, |e, cx| e.display_text(cx))
6625 );
6626 assert_eq!(
6627 cloned_snapshot
6628 .folds_in_range(0..text.len())
6629 .collect::<Vec<_>>(),
6630 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6631 );
6632 assert_set_eq!(
6633 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6634 editor.read(cx).selections.ranges(cx)
6635 );
6636 assert_set_eq!(
6637 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
6638 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
6639 );
6640 }
6641
6642 #[gpui::test]
6643 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6644 cx.set_global(Settings::test(cx));
6645 use workspace::Item;
6646 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6647 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6648
6649 cx.add_window(Default::default(), |cx| {
6650 let mut editor = build_editor(buffer.clone(), cx);
6651 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6652
6653 // Move the cursor a small distance.
6654 // Nothing is added to the navigation history.
6655 editor.change_selections(None, cx, |s| {
6656 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6657 });
6658 editor.change_selections(None, cx, |s| {
6659 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6660 });
6661 assert!(nav_history.borrow_mut().pop_backward().is_none());
6662
6663 // Move the cursor a large distance.
6664 // The history can jump back to the previous position.
6665 editor.change_selections(None, cx, |s| {
6666 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6667 });
6668 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6669 editor.navigate(nav_entry.data.unwrap(), cx);
6670 assert_eq!(nav_entry.item.id(), cx.view_id());
6671 assert_eq!(
6672 editor.selections.display_ranges(cx),
6673 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6674 );
6675 assert!(nav_history.borrow_mut().pop_backward().is_none());
6676
6677 // Move the cursor a small distance via the mouse.
6678 // Nothing is added to the navigation history.
6679 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6680 editor.end_selection(cx);
6681 assert_eq!(
6682 editor.selections.display_ranges(cx),
6683 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6684 );
6685 assert!(nav_history.borrow_mut().pop_backward().is_none());
6686
6687 // Move the cursor a large distance via the mouse.
6688 // The history can jump back to the previous position.
6689 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6690 editor.end_selection(cx);
6691 assert_eq!(
6692 editor.selections.display_ranges(cx),
6693 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6694 );
6695 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6696 editor.navigate(nav_entry.data.unwrap(), cx);
6697 assert_eq!(nav_entry.item.id(), cx.view_id());
6698 assert_eq!(
6699 editor.selections.display_ranges(cx),
6700 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6701 );
6702 assert!(nav_history.borrow_mut().pop_backward().is_none());
6703
6704 // Set scroll position to check later
6705 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6706 let original_scroll_position = editor.scroll_position;
6707 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6708
6709 // Jump to the end of the document and adjust scroll
6710 editor.move_to_end(&MoveToEnd, cx);
6711 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6712 assert_ne!(editor.scroll_position, original_scroll_position);
6713 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6714
6715 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6716 editor.navigate(nav_entry.data.unwrap(), cx);
6717 assert_eq!(editor.scroll_position, original_scroll_position);
6718 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6719
6720 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6721 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6722 invalid_anchor.text_anchor.buffer_id = Some(999);
6723 let invalid_point = Point::new(9999, 0);
6724 editor.navigate(
6725 Box::new(NavigationData {
6726 cursor_anchor: invalid_anchor.clone(),
6727 cursor_position: invalid_point,
6728 scroll_top_anchor: invalid_anchor.clone(),
6729 scroll_top_row: invalid_point.row,
6730 scroll_position: Default::default(),
6731 }),
6732 cx,
6733 );
6734 assert_eq!(
6735 editor.selections.display_ranges(cx),
6736 &[editor.max_point(cx)..editor.max_point(cx)]
6737 );
6738 assert_eq!(
6739 editor.scroll_position(cx),
6740 vec2f(0., editor.max_point(cx).row() as f32)
6741 );
6742
6743 editor
6744 });
6745 }
6746
6747 #[gpui::test]
6748 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6749 cx.set_global(Settings::test(cx));
6750 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6751 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6752
6753 view.update(cx, |view, cx| {
6754 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6755 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6756 view.end_selection(cx);
6757
6758 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6759 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6760 view.end_selection(cx);
6761 assert_eq!(
6762 view.selections.display_ranges(cx),
6763 [
6764 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6765 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6766 ]
6767 );
6768 });
6769
6770 view.update(cx, |view, cx| {
6771 view.cancel(&Cancel, cx);
6772 assert_eq!(
6773 view.selections.display_ranges(cx),
6774 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6775 );
6776 });
6777
6778 view.update(cx, |view, cx| {
6779 view.cancel(&Cancel, cx);
6780 assert_eq!(
6781 view.selections.display_ranges(cx),
6782 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6783 );
6784 });
6785 }
6786
6787 #[gpui::test]
6788 fn test_fold(cx: &mut gpui::MutableAppContext) {
6789 cx.set_global(Settings::test(cx));
6790 let buffer = MultiBuffer::build_simple(
6791 &"
6792 impl Foo {
6793 // Hello!
6794
6795 fn a() {
6796 1
6797 }
6798
6799 fn b() {
6800 2
6801 }
6802
6803 fn c() {
6804 3
6805 }
6806 }
6807 "
6808 .unindent(),
6809 cx,
6810 );
6811 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6812
6813 view.update(cx, |view, cx| {
6814 view.change_selections(None, cx, |s| {
6815 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6816 });
6817 view.fold(&Fold, cx);
6818 assert_eq!(
6819 view.display_text(cx),
6820 "
6821 impl Foo {
6822 // Hello!
6823
6824 fn a() {
6825 1
6826 }
6827
6828 fn b() {…
6829 }
6830
6831 fn c() {…
6832 }
6833 }
6834 "
6835 .unindent(),
6836 );
6837
6838 view.fold(&Fold, cx);
6839 assert_eq!(
6840 view.display_text(cx),
6841 "
6842 impl Foo {…
6843 }
6844 "
6845 .unindent(),
6846 );
6847
6848 view.unfold_lines(&UnfoldLines, cx);
6849 assert_eq!(
6850 view.display_text(cx),
6851 "
6852 impl Foo {
6853 // Hello!
6854
6855 fn a() {
6856 1
6857 }
6858
6859 fn b() {…
6860 }
6861
6862 fn c() {…
6863 }
6864 }
6865 "
6866 .unindent(),
6867 );
6868
6869 view.unfold_lines(&UnfoldLines, cx);
6870 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6871 });
6872 }
6873
6874 #[gpui::test]
6875 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6876 cx.set_global(Settings::test(cx));
6877 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6878 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6879
6880 buffer.update(cx, |buffer, cx| {
6881 buffer.edit(
6882 vec![
6883 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6884 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6885 ],
6886 cx,
6887 );
6888 });
6889
6890 view.update(cx, |view, cx| {
6891 assert_eq!(
6892 view.selections.display_ranges(cx),
6893 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6894 );
6895
6896 view.move_down(&MoveDown, cx);
6897 assert_eq!(
6898 view.selections.display_ranges(cx),
6899 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6900 );
6901
6902 view.move_right(&MoveRight, cx);
6903 assert_eq!(
6904 view.selections.display_ranges(cx),
6905 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6906 );
6907
6908 view.move_left(&MoveLeft, cx);
6909 assert_eq!(
6910 view.selections.display_ranges(cx),
6911 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6912 );
6913
6914 view.move_up(&MoveUp, cx);
6915 assert_eq!(
6916 view.selections.display_ranges(cx),
6917 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6918 );
6919
6920 view.move_to_end(&MoveToEnd, cx);
6921 assert_eq!(
6922 view.selections.display_ranges(cx),
6923 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6924 );
6925
6926 view.move_to_beginning(&MoveToBeginning, cx);
6927 assert_eq!(
6928 view.selections.display_ranges(cx),
6929 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6930 );
6931
6932 view.change_selections(None, cx, |s| {
6933 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6934 });
6935 view.select_to_beginning(&SelectToBeginning, cx);
6936 assert_eq!(
6937 view.selections.display_ranges(cx),
6938 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6939 );
6940
6941 view.select_to_end(&SelectToEnd, cx);
6942 assert_eq!(
6943 view.selections.display_ranges(cx),
6944 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6945 );
6946 });
6947 }
6948
6949 #[gpui::test]
6950 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6951 cx.set_global(Settings::test(cx));
6952 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6953 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6954
6955 assert_eq!('ⓐ'.len_utf8(), 3);
6956 assert_eq!('α'.len_utf8(), 2);
6957
6958 view.update(cx, |view, cx| {
6959 view.fold_ranges(
6960 vec![
6961 Point::new(0, 6)..Point::new(0, 12),
6962 Point::new(1, 2)..Point::new(1, 4),
6963 Point::new(2, 4)..Point::new(2, 8),
6964 ],
6965 cx,
6966 );
6967 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6968
6969 view.move_right(&MoveRight, cx);
6970 assert_eq!(
6971 view.selections.display_ranges(cx),
6972 &[empty_range(0, "ⓐ".len())]
6973 );
6974 view.move_right(&MoveRight, cx);
6975 assert_eq!(
6976 view.selections.display_ranges(cx),
6977 &[empty_range(0, "ⓐⓑ".len())]
6978 );
6979 view.move_right(&MoveRight, cx);
6980 assert_eq!(
6981 view.selections.display_ranges(cx),
6982 &[empty_range(0, "ⓐⓑ…".len())]
6983 );
6984
6985 view.move_down(&MoveDown, cx);
6986 assert_eq!(
6987 view.selections.display_ranges(cx),
6988 &[empty_range(1, "ab…".len())]
6989 );
6990 view.move_left(&MoveLeft, cx);
6991 assert_eq!(
6992 view.selections.display_ranges(cx),
6993 &[empty_range(1, "ab".len())]
6994 );
6995 view.move_left(&MoveLeft, cx);
6996 assert_eq!(
6997 view.selections.display_ranges(cx),
6998 &[empty_range(1, "a".len())]
6999 );
7000
7001 view.move_down(&MoveDown, cx);
7002 assert_eq!(
7003 view.selections.display_ranges(cx),
7004 &[empty_range(2, "α".len())]
7005 );
7006 view.move_right(&MoveRight, cx);
7007 assert_eq!(
7008 view.selections.display_ranges(cx),
7009 &[empty_range(2, "αβ".len())]
7010 );
7011 view.move_right(&MoveRight, cx);
7012 assert_eq!(
7013 view.selections.display_ranges(cx),
7014 &[empty_range(2, "αβ…".len())]
7015 );
7016 view.move_right(&MoveRight, cx);
7017 assert_eq!(
7018 view.selections.display_ranges(cx),
7019 &[empty_range(2, "αβ…ε".len())]
7020 );
7021
7022 view.move_up(&MoveUp, cx);
7023 assert_eq!(
7024 view.selections.display_ranges(cx),
7025 &[empty_range(1, "ab…e".len())]
7026 );
7027 view.move_up(&MoveUp, cx);
7028 assert_eq!(
7029 view.selections.display_ranges(cx),
7030 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7031 );
7032 view.move_left(&MoveLeft, cx);
7033 assert_eq!(
7034 view.selections.display_ranges(cx),
7035 &[empty_range(0, "ⓐⓑ…".len())]
7036 );
7037 view.move_left(&MoveLeft, cx);
7038 assert_eq!(
7039 view.selections.display_ranges(cx),
7040 &[empty_range(0, "ⓐⓑ".len())]
7041 );
7042 view.move_left(&MoveLeft, cx);
7043 assert_eq!(
7044 view.selections.display_ranges(cx),
7045 &[empty_range(0, "ⓐ".len())]
7046 );
7047 });
7048 }
7049
7050 #[gpui::test]
7051 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7052 cx.set_global(Settings::test(cx));
7053 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7054 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7055 view.update(cx, |view, cx| {
7056 view.change_selections(None, cx, |s| {
7057 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7058 });
7059 view.move_down(&MoveDown, cx);
7060 assert_eq!(
7061 view.selections.display_ranges(cx),
7062 &[empty_range(1, "abcd".len())]
7063 );
7064
7065 view.move_down(&MoveDown, cx);
7066 assert_eq!(
7067 view.selections.display_ranges(cx),
7068 &[empty_range(2, "αβγ".len())]
7069 );
7070
7071 view.move_down(&MoveDown, cx);
7072 assert_eq!(
7073 view.selections.display_ranges(cx),
7074 &[empty_range(3, "abcd".len())]
7075 );
7076
7077 view.move_down(&MoveDown, cx);
7078 assert_eq!(
7079 view.selections.display_ranges(cx),
7080 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7081 );
7082
7083 view.move_up(&MoveUp, cx);
7084 assert_eq!(
7085 view.selections.display_ranges(cx),
7086 &[empty_range(3, "abcd".len())]
7087 );
7088
7089 view.move_up(&MoveUp, cx);
7090 assert_eq!(
7091 view.selections.display_ranges(cx),
7092 &[empty_range(2, "αβγ".len())]
7093 );
7094 });
7095 }
7096
7097 #[gpui::test]
7098 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7099 cx.set_global(Settings::test(cx));
7100 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7101 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7102 view.update(cx, |view, cx| {
7103 view.change_selections(None, cx, |s| {
7104 s.select_display_ranges([
7105 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7106 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7107 ]);
7108 });
7109 });
7110
7111 view.update(cx, |view, cx| {
7112 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7113 assert_eq!(
7114 view.selections.display_ranges(cx),
7115 &[
7116 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7117 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7118 ]
7119 );
7120 });
7121
7122 view.update(cx, |view, cx| {
7123 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7124 assert_eq!(
7125 view.selections.display_ranges(cx),
7126 &[
7127 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7128 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7129 ]
7130 );
7131 });
7132
7133 view.update(cx, |view, cx| {
7134 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7135 assert_eq!(
7136 view.selections.display_ranges(cx),
7137 &[
7138 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7139 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7140 ]
7141 );
7142 });
7143
7144 view.update(cx, |view, cx| {
7145 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7146 assert_eq!(
7147 view.selections.display_ranges(cx),
7148 &[
7149 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7150 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7151 ]
7152 );
7153 });
7154
7155 // Moving to the end of line again is a no-op.
7156 view.update(cx, |view, cx| {
7157 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7158 assert_eq!(
7159 view.selections.display_ranges(cx),
7160 &[
7161 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7162 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7163 ]
7164 );
7165 });
7166
7167 view.update(cx, |view, cx| {
7168 view.move_left(&MoveLeft, cx);
7169 view.select_to_beginning_of_line(
7170 &SelectToBeginningOfLine {
7171 stop_at_soft_wraps: true,
7172 },
7173 cx,
7174 );
7175 assert_eq!(
7176 view.selections.display_ranges(cx),
7177 &[
7178 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7179 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7180 ]
7181 );
7182 });
7183
7184 view.update(cx, |view, cx| {
7185 view.select_to_beginning_of_line(
7186 &SelectToBeginningOfLine {
7187 stop_at_soft_wraps: true,
7188 },
7189 cx,
7190 );
7191 assert_eq!(
7192 view.selections.display_ranges(cx),
7193 &[
7194 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7195 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7196 ]
7197 );
7198 });
7199
7200 view.update(cx, |view, cx| {
7201 view.select_to_beginning_of_line(
7202 &SelectToBeginningOfLine {
7203 stop_at_soft_wraps: true,
7204 },
7205 cx,
7206 );
7207 assert_eq!(
7208 view.selections.display_ranges(cx),
7209 &[
7210 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7211 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7212 ]
7213 );
7214 });
7215
7216 view.update(cx, |view, cx| {
7217 view.select_to_end_of_line(
7218 &SelectToEndOfLine {
7219 stop_at_soft_wraps: true,
7220 },
7221 cx,
7222 );
7223 assert_eq!(
7224 view.selections.display_ranges(cx),
7225 &[
7226 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7227 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7228 ]
7229 );
7230 });
7231
7232 view.update(cx, |view, cx| {
7233 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7234 assert_eq!(view.display_text(cx), "ab\n de");
7235 assert_eq!(
7236 view.selections.display_ranges(cx),
7237 &[
7238 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7239 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7240 ]
7241 );
7242 });
7243
7244 view.update(cx, |view, cx| {
7245 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7246 assert_eq!(view.display_text(cx), "\n");
7247 assert_eq!(
7248 view.selections.display_ranges(cx),
7249 &[
7250 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7251 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7252 ]
7253 );
7254 });
7255 }
7256
7257 #[gpui::test]
7258 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7259 cx.set_global(Settings::test(cx));
7260 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7261 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7262 view.update(cx, |view, cx| {
7263 view.change_selections(None, cx, |s| {
7264 s.select_display_ranges([
7265 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7266 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7267 ])
7268 });
7269
7270 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7271 assert_selection_ranges(
7272 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7273 vec![('<', '>'), ('[', ']')],
7274 view,
7275 cx,
7276 );
7277
7278 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7279 assert_selection_ranges(
7280 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7281 vec![('<', '>'), ('[', ']')],
7282 view,
7283 cx,
7284 );
7285
7286 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7287 assert_selection_ranges(
7288 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7289 vec![('<', '>'), ('[', ']')],
7290 view,
7291 cx,
7292 );
7293
7294 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7295 assert_selection_ranges(
7296 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7297 vec![('<', '>'), ('[', ']')],
7298 view,
7299 cx,
7300 );
7301
7302 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7303 assert_selection_ranges(
7304 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7305 vec![('<', '>'), ('[', ']')],
7306 view,
7307 cx,
7308 );
7309
7310 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7311 assert_selection_ranges(
7312 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7313 vec![('<', '>'), ('[', ']')],
7314 view,
7315 cx,
7316 );
7317
7318 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7319 assert_selection_ranges(
7320 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7321 vec![('<', '>'), ('[', ']')],
7322 view,
7323 cx,
7324 );
7325
7326 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7327 assert_selection_ranges(
7328 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7329 vec![('<', '>'), ('[', ']')],
7330 view,
7331 cx,
7332 );
7333
7334 view.move_right(&MoveRight, cx);
7335 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7336 assert_selection_ranges(
7337 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7338 vec![('<', '>'), ('[', ']')],
7339 view,
7340 cx,
7341 );
7342
7343 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7344 assert_selection_ranges(
7345 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7346 vec![('<', '>'), ('[', ']')],
7347 view,
7348 cx,
7349 );
7350
7351 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7352 assert_selection_ranges(
7353 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7354 vec![('<', '>'), ('[', ']')],
7355 view,
7356 cx,
7357 );
7358 });
7359 }
7360
7361 #[gpui::test]
7362 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7363 cx.set_global(Settings::test(cx));
7364 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7365 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7366
7367 view.update(cx, |view, cx| {
7368 view.set_wrap_width(Some(140.), cx);
7369 assert_eq!(
7370 view.display_text(cx),
7371 "use one::{\n two::three::\n four::five\n};"
7372 );
7373
7374 view.change_selections(None, cx, |s| {
7375 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7376 });
7377
7378 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7379 assert_eq!(
7380 view.selections.display_ranges(cx),
7381 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7382 );
7383
7384 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7385 assert_eq!(
7386 view.selections.display_ranges(cx),
7387 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7388 );
7389
7390 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7391 assert_eq!(
7392 view.selections.display_ranges(cx),
7393 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7394 );
7395
7396 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7397 assert_eq!(
7398 view.selections.display_ranges(cx),
7399 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7400 );
7401
7402 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7403 assert_eq!(
7404 view.selections.display_ranges(cx),
7405 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7406 );
7407
7408 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7409 assert_eq!(
7410 view.selections.display_ranges(cx),
7411 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7412 );
7413 });
7414 }
7415
7416 #[gpui::test]
7417 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7418 cx.set_global(Settings::test(cx));
7419 let (text, ranges) = marked_text_ranges("one [two three] four");
7420 let buffer = MultiBuffer::build_simple(&text, cx);
7421
7422 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7423
7424 editor.update(cx, |editor, cx| {
7425 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7426 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7427 assert_eq!(editor.text(cx), " four");
7428 });
7429 }
7430
7431 #[gpui::test]
7432 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7433 cx.set_global(Settings::test(cx));
7434 let buffer = MultiBuffer::build_simple("one two three four", cx);
7435 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7436
7437 view.update(cx, |view, cx| {
7438 view.change_selections(None, cx, |s| {
7439 s.select_display_ranges([
7440 // an empty selection - the preceding word fragment is deleted
7441 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7442 // characters selected - they are deleted
7443 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7444 ])
7445 });
7446 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7447 });
7448
7449 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7450
7451 view.update(cx, |view, cx| {
7452 view.change_selections(None, cx, |s| {
7453 s.select_display_ranges([
7454 // an empty selection - the following word fragment is deleted
7455 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7456 // characters selected - they are deleted
7457 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7458 ])
7459 });
7460 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7461 });
7462
7463 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7464 }
7465
7466 #[gpui::test]
7467 fn test_newline(cx: &mut gpui::MutableAppContext) {
7468 cx.set_global(Settings::test(cx));
7469 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7470 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7471
7472 view.update(cx, |view, cx| {
7473 view.change_selections(None, cx, |s| {
7474 s.select_display_ranges([
7475 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7476 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7477 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7478 ])
7479 });
7480
7481 view.newline(&Newline, cx);
7482 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7483 });
7484 }
7485
7486 #[gpui::test]
7487 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7488 cx.set_global(Settings::test(cx));
7489 let buffer = MultiBuffer::build_simple(
7490 "
7491 a
7492 b(
7493 X
7494 )
7495 c(
7496 X
7497 )
7498 "
7499 .unindent()
7500 .as_str(),
7501 cx,
7502 );
7503
7504 let (_, editor) = cx.add_window(Default::default(), |cx| {
7505 let mut editor = build_editor(buffer.clone(), cx);
7506 editor.change_selections(None, cx, |s| {
7507 s.select_ranges([
7508 Point::new(2, 4)..Point::new(2, 5),
7509 Point::new(5, 4)..Point::new(5, 5),
7510 ])
7511 });
7512 editor
7513 });
7514
7515 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7516 buffer.update(cx, |buffer, cx| {
7517 buffer.edit(
7518 [
7519 (Point::new(1, 2)..Point::new(3, 0), ""),
7520 (Point::new(4, 2)..Point::new(6, 0), ""),
7521 ],
7522 cx,
7523 );
7524 assert_eq!(
7525 buffer.read(cx).text(),
7526 "
7527 a
7528 b()
7529 c()
7530 "
7531 .unindent()
7532 );
7533 });
7534
7535 editor.update(cx, |editor, cx| {
7536 assert_eq!(
7537 editor.selections.ranges(cx),
7538 &[
7539 Point::new(1, 2)..Point::new(1, 2),
7540 Point::new(2, 2)..Point::new(2, 2),
7541 ],
7542 );
7543
7544 editor.newline(&Newline, cx);
7545 assert_eq!(
7546 editor.text(cx),
7547 "
7548 a
7549 b(
7550 )
7551 c(
7552 )
7553 "
7554 .unindent()
7555 );
7556
7557 // The selections are moved after the inserted newlines
7558 assert_eq!(
7559 editor.selections.ranges(cx),
7560 &[
7561 Point::new(2, 0)..Point::new(2, 0),
7562 Point::new(4, 0)..Point::new(4, 0),
7563 ],
7564 );
7565 });
7566 }
7567
7568 #[gpui::test]
7569 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7570 cx.set_global(Settings::test(cx));
7571 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7572 let (_, editor) = cx.add_window(Default::default(), |cx| {
7573 let mut editor = build_editor(buffer.clone(), cx);
7574 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7575 editor
7576 });
7577
7578 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7579 buffer.update(cx, |buffer, cx| {
7580 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7581 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7582 });
7583
7584 editor.update(cx, |editor, cx| {
7585 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7586
7587 editor.insert("Z", cx);
7588 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7589
7590 // The selections are moved after the inserted characters
7591 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7592 });
7593 }
7594
7595 #[gpui::test]
7596 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7597 let mut cx = EditorTestContext::new(cx).await;
7598
7599 cx.set_state(indoc! {"
7600 [one} [two}
7601 three
7602 four"});
7603 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7604 cx.assert_editor_state(indoc! {"
7605 [one} [two}
7606 three
7607 four"});
7608
7609 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7610 cx.assert_editor_state(indoc! {"
7611 [one} [two}
7612 three
7613 four"});
7614
7615 // select across line ending
7616 cx.set_state(indoc! {"
7617 one two
7618 t[hree
7619 } four"});
7620 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7621 cx.assert_editor_state(indoc! {"
7622 one two
7623 t[hree
7624 } four"});
7625
7626 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7627 cx.assert_editor_state(indoc! {"
7628 one two
7629 t[hree
7630 } four"});
7631
7632 // Ensure that indenting/outdenting works when the cursor is at column 0.
7633 cx.set_state(indoc! {"
7634 one two
7635 |three
7636 four"});
7637 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7638 cx.assert_editor_state(indoc! {"
7639 one two
7640 |three
7641 four"});
7642
7643 cx.set_state(indoc! {"
7644 one two
7645 | three
7646 four"});
7647 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7648 cx.assert_editor_state(indoc! {"
7649 one two
7650 |three
7651 four"});
7652 }
7653
7654 #[gpui::test]
7655 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7656 let mut cx = EditorTestContext::new(cx).await;
7657 cx.update(|cx| {
7658 cx.update_global::<Settings, _, _>(|settings, _| {
7659 settings.language_settings.hard_tabs = Some(true);
7660 });
7661 });
7662
7663 // select two ranges on one line
7664 cx.set_state(indoc! {"
7665 [one} [two}
7666 three
7667 four"});
7668 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7669 cx.assert_editor_state(indoc! {"
7670 \t[one} [two}
7671 three
7672 four"});
7673 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7674 cx.assert_editor_state(indoc! {"
7675 \t\t[one} [two}
7676 three
7677 four"});
7678 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7679 cx.assert_editor_state(indoc! {"
7680 \t[one} [two}
7681 three
7682 four"});
7683 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7684 cx.assert_editor_state(indoc! {"
7685 [one} [two}
7686 three
7687 four"});
7688
7689 // select across a line ending
7690 cx.set_state(indoc! {"
7691 one two
7692 t[hree
7693 }four"});
7694 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7695 cx.assert_editor_state(indoc! {"
7696 one two
7697 \tt[hree
7698 }four"});
7699 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7700 cx.assert_editor_state(indoc! {"
7701 one two
7702 \t\tt[hree
7703 }four"});
7704 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7705 cx.assert_editor_state(indoc! {"
7706 one two
7707 \tt[hree
7708 }four"});
7709 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7710 cx.assert_editor_state(indoc! {"
7711 one two
7712 t[hree
7713 }four"});
7714
7715 // Ensure that indenting/outdenting works when the cursor is at column 0.
7716 cx.set_state(indoc! {"
7717 one two
7718 |three
7719 four"});
7720 cx.assert_editor_state(indoc! {"
7721 one two
7722 |three
7723 four"});
7724 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7725 cx.assert_editor_state(indoc! {"
7726 one two
7727 \t|three
7728 four"});
7729 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7730 cx.assert_editor_state(indoc! {"
7731 one two
7732 |three
7733 four"});
7734 }
7735
7736 #[gpui::test]
7737 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7738 cx.set_global(
7739 Settings::test(cx)
7740 .with_language_defaults(
7741 "TOML",
7742 LanguageSettings {
7743 tab_size: Some(2.try_into().unwrap()),
7744 ..Default::default()
7745 },
7746 )
7747 .with_language_defaults(
7748 "Rust",
7749 LanguageSettings {
7750 tab_size: Some(4.try_into().unwrap()),
7751 ..Default::default()
7752 },
7753 ),
7754 );
7755 let toml_language = Arc::new(Language::new(
7756 LanguageConfig {
7757 name: "TOML".into(),
7758 ..Default::default()
7759 },
7760 None,
7761 ));
7762 let rust_language = Arc::new(Language::new(
7763 LanguageConfig {
7764 name: "Rust".into(),
7765 ..Default::default()
7766 },
7767 None,
7768 ));
7769
7770 let toml_buffer = cx
7771 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7772 let rust_buffer = cx.add_model(|cx| {
7773 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7774 });
7775 let multibuffer = cx.add_model(|cx| {
7776 let mut multibuffer = MultiBuffer::new(0);
7777 multibuffer.push_excerpts(
7778 toml_buffer.clone(),
7779 [ExcerptRange {
7780 context: Point::new(0, 0)..Point::new(2, 0),
7781 primary: None,
7782 }],
7783 cx,
7784 );
7785 multibuffer.push_excerpts(
7786 rust_buffer.clone(),
7787 [ExcerptRange {
7788 context: Point::new(0, 0)..Point::new(1, 0),
7789 primary: None,
7790 }],
7791 cx,
7792 );
7793 multibuffer
7794 });
7795
7796 cx.add_window(Default::default(), |cx| {
7797 let mut editor = build_editor(multibuffer, cx);
7798
7799 assert_eq!(
7800 editor.text(cx),
7801 indoc! {"
7802 a = 1
7803 b = 2
7804
7805 const c: usize = 3;
7806 "}
7807 );
7808
7809 select_ranges(
7810 &mut editor,
7811 indoc! {"
7812 [a] = 1
7813 b = 2
7814
7815 [const c:] usize = 3;
7816 "},
7817 cx,
7818 );
7819
7820 editor.tab(&Tab, cx);
7821 assert_text_with_selections(
7822 &mut editor,
7823 indoc! {"
7824 [a] = 1
7825 b = 2
7826
7827 [const c:] usize = 3;
7828 "},
7829 cx,
7830 );
7831 editor.tab_prev(&TabPrev, cx);
7832 assert_text_with_selections(
7833 &mut editor,
7834 indoc! {"
7835 [a] = 1
7836 b = 2
7837
7838 [const c:] usize = 3;
7839 "},
7840 cx,
7841 );
7842
7843 editor
7844 });
7845 }
7846
7847 #[gpui::test]
7848 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7849 let mut cx = EditorTestContext::new(cx).await;
7850 // Basic backspace
7851 cx.set_state(indoc! {"
7852 on|e two three
7853 fou[r} five six
7854 seven {eight nine
7855 ]ten"});
7856 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7857 cx.assert_editor_state(indoc! {"
7858 o|e two three
7859 fou| five six
7860 seven |ten"});
7861
7862 // Test backspace inside and around indents
7863 cx.set_state(indoc! {"
7864 zero
7865 |one
7866 |two
7867 | | | three
7868 | | four"});
7869 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7870 cx.assert_editor_state(indoc! {"
7871 zero
7872 |one
7873 |two
7874 | three| four"});
7875
7876 // Test backspace with line_mode set to true
7877 cx.update_editor(|e, _| e.selections.line_mode = true);
7878 cx.set_state(indoc! {"
7879 The |quick |brown
7880 fox jumps over
7881 the lazy dog
7882 |The qu[ick b}rown"});
7883 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7884 cx.assert_editor_state(indoc! {"
7885 |fox jumps over
7886 the lazy dog|"});
7887 }
7888
7889 #[gpui::test]
7890 async fn test_delete(cx: &mut gpui::TestAppContext) {
7891 let mut cx = EditorTestContext::new(cx).await;
7892
7893 cx.set_state(indoc! {"
7894 on|e two three
7895 fou[r} five six
7896 seven {eight nine
7897 ]ten"});
7898 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7899 cx.assert_editor_state(indoc! {"
7900 on| two three
7901 fou| five six
7902 seven |ten"});
7903
7904 // Test backspace with line_mode set to true
7905 cx.update_editor(|e, _| e.selections.line_mode = true);
7906 cx.set_state(indoc! {"
7907 The |quick |brown
7908 fox {jum]ps over
7909 the lazy dog
7910 |The qu[ick b}rown"});
7911 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7912 cx.assert_editor_state("|the lazy dog|");
7913 }
7914
7915 #[gpui::test]
7916 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7917 cx.set_global(Settings::test(cx));
7918 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7919 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7920 view.update(cx, |view, cx| {
7921 view.change_selections(None, cx, |s| {
7922 s.select_display_ranges([
7923 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7924 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7925 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7926 ])
7927 });
7928 view.delete_line(&DeleteLine, cx);
7929 assert_eq!(view.display_text(cx), "ghi");
7930 assert_eq!(
7931 view.selections.display_ranges(cx),
7932 vec![
7933 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7934 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7935 ]
7936 );
7937 });
7938
7939 cx.set_global(Settings::test(cx));
7940 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7941 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7942 view.update(cx, |view, cx| {
7943 view.change_selections(None, cx, |s| {
7944 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7945 });
7946 view.delete_line(&DeleteLine, cx);
7947 assert_eq!(view.display_text(cx), "ghi\n");
7948 assert_eq!(
7949 view.selections.display_ranges(cx),
7950 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7951 );
7952 });
7953 }
7954
7955 #[gpui::test]
7956 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7957 cx.set_global(Settings::test(cx));
7958 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7959 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7960 view.update(cx, |view, cx| {
7961 view.change_selections(None, cx, |s| {
7962 s.select_display_ranges([
7963 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7964 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7965 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7966 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7967 ])
7968 });
7969 view.duplicate_line(&DuplicateLine, cx);
7970 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7971 assert_eq!(
7972 view.selections.display_ranges(cx),
7973 vec![
7974 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7975 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7976 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7977 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7978 ]
7979 );
7980 });
7981
7982 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7983 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7984 view.update(cx, |view, cx| {
7985 view.change_selections(None, cx, |s| {
7986 s.select_display_ranges([
7987 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7988 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7989 ])
7990 });
7991 view.duplicate_line(&DuplicateLine, cx);
7992 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7993 assert_eq!(
7994 view.selections.display_ranges(cx),
7995 vec![
7996 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7997 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7998 ]
7999 );
8000 });
8001 }
8002
8003 #[gpui::test]
8004 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8005 cx.set_global(Settings::test(cx));
8006 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8007 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8008 view.update(cx, |view, cx| {
8009 view.fold_ranges(
8010 vec![
8011 Point::new(0, 2)..Point::new(1, 2),
8012 Point::new(2, 3)..Point::new(4, 1),
8013 Point::new(7, 0)..Point::new(8, 4),
8014 ],
8015 cx,
8016 );
8017 view.change_selections(None, cx, |s| {
8018 s.select_display_ranges([
8019 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8020 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8021 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8022 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8023 ])
8024 });
8025 assert_eq!(
8026 view.display_text(cx),
8027 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8028 );
8029
8030 view.move_line_up(&MoveLineUp, cx);
8031 assert_eq!(
8032 view.display_text(cx),
8033 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8034 );
8035 assert_eq!(
8036 view.selections.display_ranges(cx),
8037 vec![
8038 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8039 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8040 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8041 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8042 ]
8043 );
8044 });
8045
8046 view.update(cx, |view, cx| {
8047 view.move_line_down(&MoveLineDown, cx);
8048 assert_eq!(
8049 view.display_text(cx),
8050 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8051 );
8052 assert_eq!(
8053 view.selections.display_ranges(cx),
8054 vec![
8055 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8056 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8057 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8058 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8059 ]
8060 );
8061 });
8062
8063 view.update(cx, |view, cx| {
8064 view.move_line_down(&MoveLineDown, cx);
8065 assert_eq!(
8066 view.display_text(cx),
8067 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8068 );
8069 assert_eq!(
8070 view.selections.display_ranges(cx),
8071 vec![
8072 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8073 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8074 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8075 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8076 ]
8077 );
8078 });
8079
8080 view.update(cx, |view, cx| {
8081 view.move_line_up(&MoveLineUp, cx);
8082 assert_eq!(
8083 view.display_text(cx),
8084 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8085 );
8086 assert_eq!(
8087 view.selections.display_ranges(cx),
8088 vec![
8089 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8090 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8091 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8092 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8093 ]
8094 );
8095 });
8096 }
8097
8098 #[gpui::test]
8099 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8100 cx.set_global(Settings::test(cx));
8101 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8102 let snapshot = buffer.read(cx).snapshot(cx);
8103 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8104 editor.update(cx, |editor, cx| {
8105 editor.insert_blocks(
8106 [BlockProperties {
8107 style: BlockStyle::Fixed,
8108 position: snapshot.anchor_after(Point::new(2, 0)),
8109 disposition: BlockDisposition::Below,
8110 height: 1,
8111 render: Arc::new(|_| Empty::new().boxed()),
8112 }],
8113 cx,
8114 );
8115 editor.change_selections(None, cx, |s| {
8116 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8117 });
8118 editor.move_line_down(&MoveLineDown, cx);
8119 });
8120 }
8121
8122 #[gpui::test]
8123 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8124 cx.set_global(Settings::test(cx));
8125
8126 cx.add_window(Default::default(), |cx| {
8127 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8128
8129 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8130 editor.transpose(&Default::default(), cx);
8131 assert_eq!(editor.text(cx), "bac");
8132 assert_eq!(editor.selections.ranges(cx), [2..2]);
8133
8134 editor.transpose(&Default::default(), cx);
8135 assert_eq!(editor.text(cx), "bca");
8136 assert_eq!(editor.selections.ranges(cx), [3..3]);
8137
8138 editor.transpose(&Default::default(), cx);
8139 assert_eq!(editor.text(cx), "bac");
8140 assert_eq!(editor.selections.ranges(cx), [3..3]);
8141
8142 editor
8143 })
8144 .1;
8145
8146 cx.add_window(Default::default(), |cx| {
8147 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8148
8149 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8150 editor.transpose(&Default::default(), cx);
8151 assert_eq!(editor.text(cx), "acb\nde");
8152 assert_eq!(editor.selections.ranges(cx), [3..3]);
8153
8154 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8155 editor.transpose(&Default::default(), cx);
8156 assert_eq!(editor.text(cx), "acbd\ne");
8157 assert_eq!(editor.selections.ranges(cx), [5..5]);
8158
8159 editor.transpose(&Default::default(), cx);
8160 assert_eq!(editor.text(cx), "acbde\n");
8161 assert_eq!(editor.selections.ranges(cx), [6..6]);
8162
8163 editor.transpose(&Default::default(), cx);
8164 assert_eq!(editor.text(cx), "acbd\ne");
8165 assert_eq!(editor.selections.ranges(cx), [6..6]);
8166
8167 editor
8168 })
8169 .1;
8170
8171 cx.add_window(Default::default(), |cx| {
8172 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8173
8174 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8175 editor.transpose(&Default::default(), cx);
8176 assert_eq!(editor.text(cx), "bacd\ne");
8177 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8178
8179 editor.transpose(&Default::default(), cx);
8180 assert_eq!(editor.text(cx), "bcade\n");
8181 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8182
8183 editor.transpose(&Default::default(), cx);
8184 assert_eq!(editor.text(cx), "bcda\ne");
8185 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8186
8187 editor.transpose(&Default::default(), cx);
8188 assert_eq!(editor.text(cx), "bcade\n");
8189 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8190
8191 editor.transpose(&Default::default(), cx);
8192 assert_eq!(editor.text(cx), "bcaed\n");
8193 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8194
8195 editor
8196 })
8197 .1;
8198
8199 cx.add_window(Default::default(), |cx| {
8200 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8201
8202 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8203 editor.transpose(&Default::default(), cx);
8204 assert_eq!(editor.text(cx), "🏀🍐✋");
8205 assert_eq!(editor.selections.ranges(cx), [8..8]);
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.transpose(&Default::default(), cx);
8212 assert_eq!(editor.text(cx), "🏀🍐✋");
8213 assert_eq!(editor.selections.ranges(cx), [11..11]);
8214
8215 editor
8216 })
8217 .1;
8218 }
8219
8220 #[gpui::test]
8221 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8222 let mut cx = EditorTestContext::new(cx).await;
8223
8224 cx.set_state("[one✅ }two [three }four [five }six ");
8225 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8226 cx.assert_editor_state("|two |four |six ");
8227
8228 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8229 cx.set_state("two |four |six |");
8230 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8231 cx.assert_editor_state("two one✅ |four three |six five |");
8232
8233 // Paste again but with only two cursors. Since the number of cursors doesn't
8234 // match the number of slices in the clipboard, the entire clipboard text
8235 // is pasted at each cursor.
8236 cx.set_state("|two one✅ four three six five |");
8237 cx.update_editor(|e, cx| {
8238 e.handle_input(&Input("( ".into()), cx);
8239 e.paste(&Paste, cx);
8240 e.handle_input(&Input(") ".into()), cx);
8241 });
8242 cx.assert_editor_state(indoc! {"
8243 ( one✅
8244 three
8245 five ) |two one✅ four three six five ( one✅
8246 three
8247 five ) |"});
8248
8249 // Cut with three selections, one of which is full-line.
8250 cx.set_state(indoc! {"
8251 1[2}3
8252 4|567
8253 [8}9"});
8254 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8255 cx.assert_editor_state(indoc! {"
8256 1|3
8257 |9"});
8258
8259 // Paste with three selections, noticing how the copied selection that was full-line
8260 // gets inserted before the second cursor.
8261 cx.set_state(indoc! {"
8262 1|3
8263 9|
8264 [o}ne"});
8265 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8266 cx.assert_editor_state(indoc! {"
8267 12|3
8268 4567
8269 9|
8270 8|ne"});
8271
8272 // Copy with a single cursor only, which writes the whole line into the clipboard.
8273 cx.set_state(indoc! {"
8274 The quick brown
8275 fox ju|mps over
8276 the lazy dog"});
8277 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8278 cx.assert_clipboard_content(Some("fox jumps over\n"));
8279
8280 // Paste with three selections, noticing how the copied full-line selection is inserted
8281 // before the empty selections but replaces the selection that is non-empty.
8282 cx.set_state(indoc! {"
8283 T|he quick brown
8284 [fo}x jumps over
8285 t|he lazy dog"});
8286 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8287 cx.assert_editor_state(indoc! {"
8288 fox jumps over
8289 T|he quick brown
8290 fox jumps over
8291 |x jumps over
8292 fox jumps over
8293 t|he lazy dog"});
8294 }
8295
8296 #[gpui::test]
8297 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8298 cx.set_global(Settings::test(cx));
8299 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8300 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8301 view.update(cx, |view, cx| {
8302 view.select_all(&SelectAll, cx);
8303 assert_eq!(
8304 view.selections.display_ranges(cx),
8305 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8306 );
8307 });
8308 }
8309
8310 #[gpui::test]
8311 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8312 cx.set_global(Settings::test(cx));
8313 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8314 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8315 view.update(cx, |view, cx| {
8316 view.change_selections(None, cx, |s| {
8317 s.select_display_ranges([
8318 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8319 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8320 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8321 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8322 ])
8323 });
8324 view.select_line(&SelectLine, cx);
8325 assert_eq!(
8326 view.selections.display_ranges(cx),
8327 vec![
8328 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8329 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8330 ]
8331 );
8332 });
8333
8334 view.update(cx, |view, cx| {
8335 view.select_line(&SelectLine, cx);
8336 assert_eq!(
8337 view.selections.display_ranges(cx),
8338 vec![
8339 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8340 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8341 ]
8342 );
8343 });
8344
8345 view.update(cx, |view, cx| {
8346 view.select_line(&SelectLine, cx);
8347 assert_eq!(
8348 view.selections.display_ranges(cx),
8349 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8350 );
8351 });
8352 }
8353
8354 #[gpui::test]
8355 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8356 cx.set_global(Settings::test(cx));
8357 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8358 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8359 view.update(cx, |view, cx| {
8360 view.fold_ranges(
8361 vec![
8362 Point::new(0, 2)..Point::new(1, 2),
8363 Point::new(2, 3)..Point::new(4, 1),
8364 Point::new(7, 0)..Point::new(8, 4),
8365 ],
8366 cx,
8367 );
8368 view.change_selections(None, cx, |s| {
8369 s.select_display_ranges([
8370 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8371 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8372 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8373 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8374 ])
8375 });
8376 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8377 });
8378
8379 view.update(cx, |view, cx| {
8380 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8381 assert_eq!(
8382 view.display_text(cx),
8383 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8384 );
8385 assert_eq!(
8386 view.selections.display_ranges(cx),
8387 [
8388 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8389 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8390 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8391 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8392 ]
8393 );
8394 });
8395
8396 view.update(cx, |view, cx| {
8397 view.change_selections(None, cx, |s| {
8398 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8399 });
8400 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8401 assert_eq!(
8402 view.display_text(cx),
8403 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8404 );
8405 assert_eq!(
8406 view.selections.display_ranges(cx),
8407 [
8408 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8409 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8410 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8411 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8412 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8413 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8414 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8415 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8416 ]
8417 );
8418 });
8419 }
8420
8421 #[gpui::test]
8422 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8423 cx.set_global(Settings::test(cx));
8424 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8425 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8426
8427 view.update(cx, |view, cx| {
8428 view.change_selections(None, cx, |s| {
8429 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8430 });
8431 });
8432 view.update(cx, |view, cx| {
8433 view.add_selection_above(&AddSelectionAbove, cx);
8434 assert_eq!(
8435 view.selections.display_ranges(cx),
8436 vec![
8437 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8438 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8439 ]
8440 );
8441 });
8442
8443 view.update(cx, |view, cx| {
8444 view.add_selection_above(&AddSelectionAbove, cx);
8445 assert_eq!(
8446 view.selections.display_ranges(cx),
8447 vec![
8448 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8449 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8450 ]
8451 );
8452 });
8453
8454 view.update(cx, |view, cx| {
8455 view.add_selection_below(&AddSelectionBelow, cx);
8456 assert_eq!(
8457 view.selections.display_ranges(cx),
8458 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8459 );
8460
8461 view.undo_selection(&UndoSelection, cx);
8462 assert_eq!(
8463 view.selections.display_ranges(cx),
8464 vec![
8465 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8466 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8467 ]
8468 );
8469
8470 view.redo_selection(&RedoSelection, cx);
8471 assert_eq!(
8472 view.selections.display_ranges(cx),
8473 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8474 );
8475 });
8476
8477 view.update(cx, |view, cx| {
8478 view.add_selection_below(&AddSelectionBelow, cx);
8479 assert_eq!(
8480 view.selections.display_ranges(cx),
8481 vec![
8482 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8483 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8484 ]
8485 );
8486 });
8487
8488 view.update(cx, |view, cx| {
8489 view.add_selection_below(&AddSelectionBelow, cx);
8490 assert_eq!(
8491 view.selections.display_ranges(cx),
8492 vec![
8493 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8494 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8495 ]
8496 );
8497 });
8498
8499 view.update(cx, |view, cx| {
8500 view.change_selections(None, cx, |s| {
8501 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8502 });
8503 });
8504 view.update(cx, |view, cx| {
8505 view.add_selection_below(&AddSelectionBelow, cx);
8506 assert_eq!(
8507 view.selections.display_ranges(cx),
8508 vec![
8509 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8510 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8511 ]
8512 );
8513 });
8514
8515 view.update(cx, |view, cx| {
8516 view.add_selection_below(&AddSelectionBelow, cx);
8517 assert_eq!(
8518 view.selections.display_ranges(cx),
8519 vec![
8520 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8521 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8522 ]
8523 );
8524 });
8525
8526 view.update(cx, |view, cx| {
8527 view.add_selection_above(&AddSelectionAbove, cx);
8528 assert_eq!(
8529 view.selections.display_ranges(cx),
8530 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8531 );
8532 });
8533
8534 view.update(cx, |view, cx| {
8535 view.add_selection_above(&AddSelectionAbove, cx);
8536 assert_eq!(
8537 view.selections.display_ranges(cx),
8538 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8539 );
8540 });
8541
8542 view.update(cx, |view, cx| {
8543 view.change_selections(None, cx, |s| {
8544 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8545 });
8546 view.add_selection_below(&AddSelectionBelow, cx);
8547 assert_eq!(
8548 view.selections.display_ranges(cx),
8549 vec![
8550 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8551 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8552 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8553 ]
8554 );
8555 });
8556
8557 view.update(cx, |view, cx| {
8558 view.add_selection_below(&AddSelectionBelow, cx);
8559 assert_eq!(
8560 view.selections.display_ranges(cx),
8561 vec![
8562 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8563 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8564 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8565 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8566 ]
8567 );
8568 });
8569
8570 view.update(cx, |view, cx| {
8571 view.add_selection_above(&AddSelectionAbove, cx);
8572 assert_eq!(
8573 view.selections.display_ranges(cx),
8574 vec![
8575 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8576 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8577 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8578 ]
8579 );
8580 });
8581
8582 view.update(cx, |view, cx| {
8583 view.change_selections(None, cx, |s| {
8584 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8585 });
8586 });
8587 view.update(cx, |view, cx| {
8588 view.add_selection_above(&AddSelectionAbove, cx);
8589 assert_eq!(
8590 view.selections.display_ranges(cx),
8591 vec![
8592 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8593 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8594 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8595 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8596 ]
8597 );
8598 });
8599
8600 view.update(cx, |view, cx| {
8601 view.add_selection_below(&AddSelectionBelow, cx);
8602 assert_eq!(
8603 view.selections.display_ranges(cx),
8604 vec![
8605 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8606 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8607 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8608 ]
8609 );
8610 });
8611 }
8612
8613 #[gpui::test]
8614 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8615 cx.set_global(Settings::test(cx));
8616
8617 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8618 let buffer = MultiBuffer::build_simple(&text, cx);
8619 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8620
8621 view.update(cx, |view, cx| {
8622 view.change_selections(None, cx, |s| {
8623 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8624 });
8625 view.select_next(
8626 &SelectNext {
8627 replace_newest: false,
8628 },
8629 cx,
8630 );
8631 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8632
8633 view.select_next(
8634 &SelectNext {
8635 replace_newest: false,
8636 },
8637 cx,
8638 );
8639 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8640
8641 view.undo_selection(&UndoSelection, cx);
8642 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8643
8644 view.redo_selection(&RedoSelection, cx);
8645 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8646
8647 view.select_next(
8648 &SelectNext {
8649 replace_newest: false,
8650 },
8651 cx,
8652 );
8653 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8654
8655 view.select_next(
8656 &SelectNext {
8657 replace_newest: false,
8658 },
8659 cx,
8660 );
8661 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8662 });
8663 }
8664
8665 #[gpui::test]
8666 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8667 cx.update(|cx| cx.set_global(Settings::test(cx)));
8668 let language = Arc::new(Language::new(
8669 LanguageConfig::default(),
8670 Some(tree_sitter_rust::language()),
8671 ));
8672
8673 let text = r#"
8674 use mod1::mod2::{mod3, mod4};
8675
8676 fn fn_1(param1: bool, param2: &str) {
8677 let var1 = "text";
8678 }
8679 "#
8680 .unindent();
8681
8682 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8683 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8684 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8685 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8686 .await;
8687
8688 view.update(cx, |view, cx| {
8689 view.change_selections(None, cx, |s| {
8690 s.select_display_ranges([
8691 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8692 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8693 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8694 ]);
8695 });
8696 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8697 });
8698 assert_eq!(
8699 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8700 &[
8701 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8702 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8703 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8704 ]
8705 );
8706
8707 view.update(cx, |view, cx| {
8708 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8709 });
8710 assert_eq!(
8711 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8712 &[
8713 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8714 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8715 ]
8716 );
8717
8718 view.update(cx, |view, cx| {
8719 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8720 });
8721 assert_eq!(
8722 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8723 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8724 );
8725
8726 // Trying to expand the selected syntax node one more time has no effect.
8727 view.update(cx, |view, cx| {
8728 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8729 });
8730 assert_eq!(
8731 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8732 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8733 );
8734
8735 view.update(cx, |view, cx| {
8736 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8737 });
8738 assert_eq!(
8739 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8740 &[
8741 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8742 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8743 ]
8744 );
8745
8746 view.update(cx, |view, cx| {
8747 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8748 });
8749 assert_eq!(
8750 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8751 &[
8752 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8753 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8754 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8755 ]
8756 );
8757
8758 view.update(cx, |view, cx| {
8759 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8760 });
8761 assert_eq!(
8762 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8763 &[
8764 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8765 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8766 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8767 ]
8768 );
8769
8770 // Trying to shrink the selected syntax node one more time has no effect.
8771 view.update(cx, |view, cx| {
8772 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8773 });
8774 assert_eq!(
8775 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8776 &[
8777 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8778 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8779 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8780 ]
8781 );
8782
8783 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8784 // a fold.
8785 view.update(cx, |view, cx| {
8786 view.fold_ranges(
8787 vec![
8788 Point::new(0, 21)..Point::new(0, 24),
8789 Point::new(3, 20)..Point::new(3, 22),
8790 ],
8791 cx,
8792 );
8793 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8794 });
8795 assert_eq!(
8796 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8797 &[
8798 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8799 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8800 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8801 ]
8802 );
8803 }
8804
8805 #[gpui::test]
8806 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8807 cx.update(|cx| cx.set_global(Settings::test(cx)));
8808 let language = Arc::new(
8809 Language::new(
8810 LanguageConfig {
8811 brackets: vec![
8812 BracketPair {
8813 start: "{".to_string(),
8814 end: "}".to_string(),
8815 close: false,
8816 newline: true,
8817 },
8818 BracketPair {
8819 start: "(".to_string(),
8820 end: ")".to_string(),
8821 close: false,
8822 newline: true,
8823 },
8824 ],
8825 ..Default::default()
8826 },
8827 Some(tree_sitter_rust::language()),
8828 )
8829 .with_indents_query(
8830 r#"
8831 (_ "(" ")" @end) @indent
8832 (_ "{" "}" @end) @indent
8833 "#,
8834 )
8835 .unwrap(),
8836 );
8837
8838 let text = "fn a() {}";
8839
8840 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8841 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8842 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8843 editor
8844 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8845 .await;
8846
8847 editor.update(cx, |editor, cx| {
8848 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8849 editor.newline(&Newline, cx);
8850 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8851 assert_eq!(
8852 editor.selections.ranges(cx),
8853 &[
8854 Point::new(1, 4)..Point::new(1, 4),
8855 Point::new(3, 4)..Point::new(3, 4),
8856 Point::new(5, 0)..Point::new(5, 0)
8857 ]
8858 );
8859 });
8860 }
8861
8862 #[gpui::test]
8863 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8864 cx.update(|cx| cx.set_global(Settings::test(cx)));
8865 let language = Arc::new(Language::new(
8866 LanguageConfig {
8867 brackets: vec![
8868 BracketPair {
8869 start: "{".to_string(),
8870 end: "}".to_string(),
8871 close: true,
8872 newline: true,
8873 },
8874 BracketPair {
8875 start: "/*".to_string(),
8876 end: " */".to_string(),
8877 close: true,
8878 newline: true,
8879 },
8880 BracketPair {
8881 start: "[".to_string(),
8882 end: "]".to_string(),
8883 close: false,
8884 newline: true,
8885 },
8886 ],
8887 autoclose_before: "})]".to_string(),
8888 ..Default::default()
8889 },
8890 Some(tree_sitter_rust::language()),
8891 ));
8892
8893 let text = r#"
8894 a
8895
8896 /
8897
8898 "#
8899 .unindent();
8900
8901 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8902 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8903 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8904 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8905 .await;
8906
8907 view.update(cx, |view, cx| {
8908 view.change_selections(None, cx, |s| {
8909 s.select_display_ranges([
8910 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8911 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8912 ])
8913 });
8914
8915 view.handle_input(&Input("{".to_string()), cx);
8916 view.handle_input(&Input("{".to_string()), cx);
8917 view.handle_input(&Input("{".to_string()), cx);
8918 assert_eq!(
8919 view.text(cx),
8920 "
8921 {{{}}}
8922 {{{}}}
8923 /
8924
8925 "
8926 .unindent()
8927 );
8928
8929 view.move_right(&MoveRight, cx);
8930 view.handle_input(&Input("}".to_string()), cx);
8931 view.handle_input(&Input("}".to_string()), cx);
8932 view.handle_input(&Input("}".to_string()), cx);
8933 assert_eq!(
8934 view.text(cx),
8935 "
8936 {{{}}}}
8937 {{{}}}}
8938 /
8939
8940 "
8941 .unindent()
8942 );
8943
8944 view.undo(&Undo, cx);
8945 view.handle_input(&Input("/".to_string()), cx);
8946 view.handle_input(&Input("*".to_string()), cx);
8947 assert_eq!(
8948 view.text(cx),
8949 "
8950 /* */
8951 /* */
8952 /
8953
8954 "
8955 .unindent()
8956 );
8957
8958 view.undo(&Undo, cx);
8959 view.change_selections(None, cx, |s| {
8960 s.select_display_ranges([
8961 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8962 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8963 ])
8964 });
8965 view.handle_input(&Input("*".to_string()), cx);
8966 assert_eq!(
8967 view.text(cx),
8968 "
8969 a
8970
8971 /*
8972 *
8973 "
8974 .unindent()
8975 );
8976
8977 // Don't autoclose if the next character isn't whitespace and isn't
8978 // listed in the language's "autoclose_before" section.
8979 view.finalize_last_transaction(cx);
8980 view.change_selections(None, cx, |s| {
8981 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8982 });
8983 view.handle_input(&Input("{".to_string()), cx);
8984 assert_eq!(
8985 view.text(cx),
8986 "
8987 {a
8988
8989 /*
8990 *
8991 "
8992 .unindent()
8993 );
8994
8995 view.undo(&Undo, cx);
8996 view.change_selections(None, cx, |s| {
8997 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8998 });
8999 view.handle_input(&Input("{".to_string()), cx);
9000 assert_eq!(
9001 view.text(cx),
9002 "
9003 {a}
9004
9005 /*
9006 *
9007 "
9008 .unindent()
9009 );
9010 assert_eq!(
9011 view.selections.display_ranges(cx),
9012 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9013 );
9014
9015 view.undo(&Undo, cx);
9016 view.handle_input(&Input("[".to_string()), cx);
9017 assert_eq!(
9018 view.text(cx),
9019 "
9020 [a]
9021
9022 /*
9023 *
9024 "
9025 .unindent()
9026 );
9027 assert_eq!(
9028 view.selections.display_ranges(cx),
9029 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9030 );
9031
9032 view.undo(&Undo, cx);
9033 view.change_selections(None, cx, |s| {
9034 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9035 });
9036 view.handle_input(&Input("[".to_string()), cx);
9037 assert_eq!(
9038 view.text(cx),
9039 "
9040 a[
9041
9042 /*
9043 *
9044 "
9045 .unindent()
9046 );
9047 assert_eq!(
9048 view.selections.display_ranges(cx),
9049 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9050 );
9051 });
9052 }
9053
9054 #[gpui::test]
9055 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9056 cx.update(|cx| cx.set_global(Settings::test(cx)));
9057 let language = Arc::new(Language::new(
9058 LanguageConfig {
9059 brackets: vec![BracketPair {
9060 start: "{".to_string(),
9061 end: "}".to_string(),
9062 close: true,
9063 newline: true,
9064 }],
9065 ..Default::default()
9066 },
9067 Some(tree_sitter_rust::language()),
9068 ));
9069
9070 let text = r#"
9071 a
9072 b
9073 c
9074 "#
9075 .unindent();
9076
9077 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9078 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9079 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9080 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9081 .await;
9082
9083 view.update(cx, |view, cx| {
9084 view.change_selections(None, cx, |s| {
9085 s.select_display_ranges([
9086 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9087 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9088 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9089 ])
9090 });
9091
9092 view.handle_input(&Input("{".to_string()), cx);
9093 view.handle_input(&Input("{".to_string()), cx);
9094 view.handle_input(&Input("{".to_string()), cx);
9095 assert_eq!(
9096 view.text(cx),
9097 "
9098 {{{a}}}
9099 {{{b}}}
9100 {{{c}}}
9101 "
9102 .unindent()
9103 );
9104 assert_eq!(
9105 view.selections.display_ranges(cx),
9106 [
9107 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9108 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9109 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9110 ]
9111 );
9112
9113 view.undo(&Undo, cx);
9114 assert_eq!(
9115 view.text(cx),
9116 "
9117 a
9118 b
9119 c
9120 "
9121 .unindent()
9122 );
9123 assert_eq!(
9124 view.selections.display_ranges(cx),
9125 [
9126 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9127 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9128 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9129 ]
9130 );
9131 });
9132 }
9133
9134 #[gpui::test]
9135 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9136 cx.update(|cx| cx.set_global(Settings::test(cx)));
9137 let language = Arc::new(Language::new(
9138 LanguageConfig {
9139 brackets: vec![BracketPair {
9140 start: "{".to_string(),
9141 end: "}".to_string(),
9142 close: true,
9143 newline: true,
9144 }],
9145 autoclose_before: "}".to_string(),
9146 ..Default::default()
9147 },
9148 Some(tree_sitter_rust::language()),
9149 ));
9150
9151 let text = r#"
9152 a
9153 b
9154 c
9155 "#
9156 .unindent();
9157
9158 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9159 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9160 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9161 editor
9162 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9163 .await;
9164
9165 editor.update(cx, |editor, cx| {
9166 editor.change_selections(None, cx, |s| {
9167 s.select_ranges([
9168 Point::new(0, 1)..Point::new(0, 1),
9169 Point::new(1, 1)..Point::new(1, 1),
9170 Point::new(2, 1)..Point::new(2, 1),
9171 ])
9172 });
9173
9174 editor.handle_input(&Input("{".to_string()), cx);
9175 editor.handle_input(&Input("{".to_string()), cx);
9176 editor.handle_input(&Input("_".to_string()), cx);
9177 assert_eq!(
9178 editor.text(cx),
9179 "
9180 a{{_}}
9181 b{{_}}
9182 c{{_}}
9183 "
9184 .unindent()
9185 );
9186 assert_eq!(
9187 editor.selections.ranges::<Point>(cx),
9188 [
9189 Point::new(0, 4)..Point::new(0, 4),
9190 Point::new(1, 4)..Point::new(1, 4),
9191 Point::new(2, 4)..Point::new(2, 4)
9192 ]
9193 );
9194
9195 editor.backspace(&Default::default(), cx);
9196 editor.backspace(&Default::default(), cx);
9197 assert_eq!(
9198 editor.text(cx),
9199 "
9200 a{}
9201 b{}
9202 c{}
9203 "
9204 .unindent()
9205 );
9206 assert_eq!(
9207 editor.selections.ranges::<Point>(cx),
9208 [
9209 Point::new(0, 2)..Point::new(0, 2),
9210 Point::new(1, 2)..Point::new(1, 2),
9211 Point::new(2, 2)..Point::new(2, 2)
9212 ]
9213 );
9214
9215 editor.delete_to_previous_word_start(&Default::default(), cx);
9216 assert_eq!(
9217 editor.text(cx),
9218 "
9219 a
9220 b
9221 c
9222 "
9223 .unindent()
9224 );
9225 assert_eq!(
9226 editor.selections.ranges::<Point>(cx),
9227 [
9228 Point::new(0, 1)..Point::new(0, 1),
9229 Point::new(1, 1)..Point::new(1, 1),
9230 Point::new(2, 1)..Point::new(2, 1)
9231 ]
9232 );
9233 });
9234 }
9235
9236 #[gpui::test]
9237 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9238 cx.update(|cx| cx.set_global(Settings::test(cx)));
9239
9240 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9241 a.| b
9242 a.| b
9243 a.| b"});
9244 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9245 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9246
9247 editor.update(cx, |editor, cx| {
9248 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9249
9250 editor
9251 .insert_snippet(&insertion_ranges, snippet, cx)
9252 .unwrap();
9253
9254 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9255 let range_markers = ('<', '>');
9256 let (expected_text, mut selection_ranges_lookup) =
9257 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9258 let selection_ranges = selection_ranges_lookup
9259 .remove(&range_markers.into())
9260 .unwrap();
9261 assert_eq!(editor.text(cx), expected_text);
9262 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9263 }
9264 assert(
9265 editor,
9266 cx,
9267 indoc! {"
9268 a.f(<one>, two, <three>) b
9269 a.f(<one>, two, <three>) b
9270 a.f(<one>, two, <three>) b"},
9271 );
9272
9273 // Can't move earlier than the first tab stop
9274 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9275 assert(
9276 editor,
9277 cx,
9278 indoc! {"
9279 a.f(<one>, two, <three>) b
9280 a.f(<one>, two, <three>) b
9281 a.f(<one>, two, <three>) b"},
9282 );
9283
9284 assert!(editor.move_to_next_snippet_tabstop(cx));
9285 assert(
9286 editor,
9287 cx,
9288 indoc! {"
9289 a.f(one, <two>, three) b
9290 a.f(one, <two>, three) b
9291 a.f(one, <two>, three) b"},
9292 );
9293
9294 editor.move_to_prev_snippet_tabstop(cx);
9295 assert(
9296 editor,
9297 cx,
9298 indoc! {"
9299 a.f(<one>, two, <three>) b
9300 a.f(<one>, two, <three>) b
9301 a.f(<one>, two, <three>) b"},
9302 );
9303
9304 assert!(editor.move_to_next_snippet_tabstop(cx));
9305 assert(
9306 editor,
9307 cx,
9308 indoc! {"
9309 a.f(one, <two>, three) b
9310 a.f(one, <two>, three) b
9311 a.f(one, <two>, three) b"},
9312 );
9313 assert!(editor.move_to_next_snippet_tabstop(cx));
9314 assert(
9315 editor,
9316 cx,
9317 indoc! {"
9318 a.f(one, two, three)<> b
9319 a.f(one, two, three)<> b
9320 a.f(one, two, three)<> b"},
9321 );
9322
9323 // As soon as the last tab stop is reached, snippet state is gone
9324 editor.move_to_prev_snippet_tabstop(cx);
9325 assert(
9326 editor,
9327 cx,
9328 indoc! {"
9329 a.f(one, two, three)<> b
9330 a.f(one, two, three)<> b
9331 a.f(one, two, three)<> b"},
9332 );
9333 });
9334 }
9335
9336 #[gpui::test]
9337 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9338 cx.foreground().forbid_parking();
9339
9340 let mut language = Language::new(
9341 LanguageConfig {
9342 name: "Rust".into(),
9343 path_suffixes: vec!["rs".to_string()],
9344 ..Default::default()
9345 },
9346 Some(tree_sitter_rust::language()),
9347 );
9348 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9349 capabilities: lsp::ServerCapabilities {
9350 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9351 ..Default::default()
9352 },
9353 ..Default::default()
9354 });
9355
9356 let fs = FakeFs::new(cx.background().clone());
9357 fs.insert_file("/file.rs", Default::default()).await;
9358
9359 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9360 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9361 let buffer = project
9362 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9363 .await
9364 .unwrap();
9365
9366 cx.foreground().start_waiting();
9367 let fake_server = fake_servers.next().await.unwrap();
9368
9369 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9370 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9371 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9372 assert!(cx.read(|cx| editor.is_dirty(cx)));
9373
9374 let save = cx.update(|cx| editor.save(project.clone(), cx));
9375 fake_server
9376 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9377 assert_eq!(
9378 params.text_document.uri,
9379 lsp::Url::from_file_path("/file.rs").unwrap()
9380 );
9381 assert_eq!(params.options.tab_size, 4);
9382 Ok(Some(vec![lsp::TextEdit::new(
9383 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9384 ", ".to_string(),
9385 )]))
9386 })
9387 .next()
9388 .await;
9389 cx.foreground().start_waiting();
9390 save.await.unwrap();
9391 assert_eq!(
9392 editor.read_with(cx, |editor, cx| editor.text(cx)),
9393 "one, two\nthree\n"
9394 );
9395 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9396
9397 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9398 assert!(cx.read(|cx| editor.is_dirty(cx)));
9399
9400 // Ensure we can still save even if formatting hangs.
9401 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9402 assert_eq!(
9403 params.text_document.uri,
9404 lsp::Url::from_file_path("/file.rs").unwrap()
9405 );
9406 futures::future::pending::<()>().await;
9407 unreachable!()
9408 });
9409 let save = cx.update(|cx| editor.save(project.clone(), cx));
9410 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9411 cx.foreground().start_waiting();
9412 save.await.unwrap();
9413 assert_eq!(
9414 editor.read_with(cx, |editor, cx| editor.text(cx)),
9415 "one\ntwo\nthree\n"
9416 );
9417 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9418
9419 // Set rust language override and assert overriden tabsize is sent to language server
9420 cx.update(|cx| {
9421 cx.update_global::<Settings, _, _>(|settings, _| {
9422 settings.language_overrides.insert(
9423 "Rust".into(),
9424 LanguageSettings {
9425 tab_size: Some(8.try_into().unwrap()),
9426 ..Default::default()
9427 },
9428 );
9429 })
9430 });
9431
9432 let save = cx.update(|cx| editor.save(project.clone(), cx));
9433 fake_server
9434 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9435 assert_eq!(
9436 params.text_document.uri,
9437 lsp::Url::from_file_path("/file.rs").unwrap()
9438 );
9439 assert_eq!(params.options.tab_size, 8);
9440 Ok(Some(vec![]))
9441 })
9442 .next()
9443 .await;
9444 cx.foreground().start_waiting();
9445 save.await.unwrap();
9446 }
9447
9448 #[gpui::test]
9449 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9450 cx.foreground().forbid_parking();
9451
9452 let mut language = Language::new(
9453 LanguageConfig {
9454 name: "Rust".into(),
9455 path_suffixes: vec!["rs".to_string()],
9456 ..Default::default()
9457 },
9458 Some(tree_sitter_rust::language()),
9459 );
9460 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9461 capabilities: lsp::ServerCapabilities {
9462 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9463 ..Default::default()
9464 },
9465 ..Default::default()
9466 });
9467
9468 let fs = FakeFs::new(cx.background().clone());
9469 fs.insert_file("/file.rs", Default::default()).await;
9470
9471 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9472 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9473 let buffer = project
9474 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9475 .await
9476 .unwrap();
9477
9478 cx.foreground().start_waiting();
9479 let fake_server = fake_servers.next().await.unwrap();
9480
9481 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9482 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9483 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9484 assert!(cx.read(|cx| editor.is_dirty(cx)));
9485
9486 let save = cx.update(|cx| editor.save(project.clone(), cx));
9487 fake_server
9488 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9489 assert_eq!(
9490 params.text_document.uri,
9491 lsp::Url::from_file_path("/file.rs").unwrap()
9492 );
9493 assert_eq!(params.options.tab_size, 4);
9494 Ok(Some(vec![lsp::TextEdit::new(
9495 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9496 ", ".to_string(),
9497 )]))
9498 })
9499 .next()
9500 .await;
9501 cx.foreground().start_waiting();
9502 save.await.unwrap();
9503 assert_eq!(
9504 editor.read_with(cx, |editor, cx| editor.text(cx)),
9505 "one, two\nthree\n"
9506 );
9507 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9508
9509 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9510 assert!(cx.read(|cx| editor.is_dirty(cx)));
9511
9512 // Ensure we can still save even if formatting hangs.
9513 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9514 move |params, _| async move {
9515 assert_eq!(
9516 params.text_document.uri,
9517 lsp::Url::from_file_path("/file.rs").unwrap()
9518 );
9519 futures::future::pending::<()>().await;
9520 unreachable!()
9521 },
9522 );
9523 let save = cx.update(|cx| editor.save(project.clone(), cx));
9524 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9525 cx.foreground().start_waiting();
9526 save.await.unwrap();
9527 assert_eq!(
9528 editor.read_with(cx, |editor, cx| editor.text(cx)),
9529 "one\ntwo\nthree\n"
9530 );
9531 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9532
9533 // Set rust language override and assert overriden tabsize is sent to language server
9534 cx.update(|cx| {
9535 cx.update_global::<Settings, _, _>(|settings, _| {
9536 settings.language_overrides.insert(
9537 "Rust".into(),
9538 LanguageSettings {
9539 tab_size: Some(8.try_into().unwrap()),
9540 ..Default::default()
9541 },
9542 );
9543 })
9544 });
9545
9546 let save = cx.update(|cx| editor.save(project.clone(), cx));
9547 fake_server
9548 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9549 assert_eq!(
9550 params.text_document.uri,
9551 lsp::Url::from_file_path("/file.rs").unwrap()
9552 );
9553 assert_eq!(params.options.tab_size, 8);
9554 Ok(Some(vec![]))
9555 })
9556 .next()
9557 .await;
9558 cx.foreground().start_waiting();
9559 save.await.unwrap();
9560 }
9561
9562 #[gpui::test]
9563 async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
9564 deterministic.forbid_parking();
9565
9566 let fs = FakeFs::new(cx.background().clone());
9567 fs.insert_file("/file.rs", Default::default()).await;
9568
9569 let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await;
9570 let buffer = project
9571 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9572 .await
9573 .unwrap();
9574
9575 let (_, editor) = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
9576
9577 // Autosave on window change.
9578 editor.update(cx, |editor, cx| {
9579 cx.update_global(|settings: &mut Settings, _| {
9580 settings.autosave = Autosave::OnWindowChange;
9581 });
9582 editor.insert("X", cx);
9583 assert!(editor.is_dirty(cx))
9584 });
9585
9586 // Deactivating the window saves the file.
9587 cx.simulate_window_activation(None);
9588 deterministic.run_until_parked();
9589 assert_eq!(fs.load(Path::new("/file.rs")).await.unwrap(), "X");
9590 editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
9591
9592 // Autosave on focus change.
9593 editor.update(cx, |editor, cx| {
9594 cx.focus_self();
9595 cx.update_global(|settings: &mut Settings, _| {
9596 settings.autosave = Autosave::OnFocusChange;
9597 });
9598 editor.insert("X", cx);
9599 assert!(editor.is_dirty(cx))
9600 });
9601
9602 // Blurring the editor saves the file.
9603 editor.update(cx, |_, cx| cx.blur());
9604 deterministic.run_until_parked();
9605 assert_eq!(fs.load(Path::new("/file.rs")).await.unwrap(), "XX");
9606 editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
9607
9608 // Autosave after delay.
9609 editor.update(cx, |editor, cx| {
9610 cx.update_global(|settings: &mut Settings, _| {
9611 settings.autosave = Autosave::AfterDelay { milliseconds: 500 };
9612 });
9613 editor.insert("X", cx);
9614 assert!(editor.is_dirty(cx))
9615 });
9616
9617 // Delay hasn't fully expired, so the file is still dirty and unsaved.
9618 deterministic.advance_clock(Duration::from_millis(250));
9619 assert_eq!(fs.load(Path::new("/file.rs")).await.unwrap(), "XX");
9620 editor.read_with(cx, |editor, cx| assert!(editor.is_dirty(cx)));
9621
9622 // After delay expires, the file is saved.
9623 deterministic.advance_clock(Duration::from_millis(250));
9624 assert_eq!(fs.load(Path::new("/file.rs")).await.unwrap(), "XXX");
9625 editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
9626 }
9627
9628 #[gpui::test]
9629 async fn test_completion(cx: &mut gpui::TestAppContext) {
9630 let mut language = Language::new(
9631 LanguageConfig {
9632 name: "Rust".into(),
9633 path_suffixes: vec!["rs".to_string()],
9634 ..Default::default()
9635 },
9636 Some(tree_sitter_rust::language()),
9637 );
9638 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9639 capabilities: lsp::ServerCapabilities {
9640 completion_provider: Some(lsp::CompletionOptions {
9641 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9642 ..Default::default()
9643 }),
9644 ..Default::default()
9645 },
9646 ..Default::default()
9647 });
9648
9649 let text = "
9650 one
9651 two
9652 three
9653 "
9654 .unindent();
9655
9656 let fs = FakeFs::new(cx.background().clone());
9657 fs.insert_file("/file.rs", text).await;
9658
9659 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9660 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9661 let buffer = project
9662 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9663 .await
9664 .unwrap();
9665 let mut fake_server = fake_servers.next().await.unwrap();
9666
9667 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9668 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9669
9670 editor.update(cx, |editor, cx| {
9671 editor.project = Some(project);
9672 editor.change_selections(None, cx, |s| {
9673 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9674 });
9675 editor.handle_input(&Input(".".to_string()), cx);
9676 });
9677
9678 handle_completion_request(
9679 &mut fake_server,
9680 "/file.rs",
9681 Point::new(0, 4),
9682 vec![
9683 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9684 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9685 ],
9686 )
9687 .await;
9688 editor
9689 .condition(&cx, |editor, _| editor.context_menu_visible())
9690 .await;
9691
9692 let apply_additional_edits = editor.update(cx, |editor, cx| {
9693 editor.move_down(&MoveDown, cx);
9694 let apply_additional_edits = editor
9695 .confirm_completion(&ConfirmCompletion::default(), cx)
9696 .unwrap();
9697 assert_eq!(
9698 editor.text(cx),
9699 "
9700 one.second_completion
9701 two
9702 three
9703 "
9704 .unindent()
9705 );
9706 apply_additional_edits
9707 });
9708
9709 handle_resolve_completion_request(
9710 &mut fake_server,
9711 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9712 )
9713 .await;
9714 apply_additional_edits.await.unwrap();
9715 assert_eq!(
9716 editor.read_with(cx, |editor, cx| editor.text(cx)),
9717 "
9718 one.second_completion
9719 two
9720 three
9721 additional edit
9722 "
9723 .unindent()
9724 );
9725
9726 editor.update(cx, |editor, cx| {
9727 editor.change_selections(None, cx, |s| {
9728 s.select_ranges([
9729 Point::new(1, 3)..Point::new(1, 3),
9730 Point::new(2, 5)..Point::new(2, 5),
9731 ])
9732 });
9733
9734 editor.handle_input(&Input(" ".to_string()), cx);
9735 assert!(editor.context_menu.is_none());
9736 editor.handle_input(&Input("s".to_string()), cx);
9737 assert!(editor.context_menu.is_none());
9738 });
9739
9740 handle_completion_request(
9741 &mut fake_server,
9742 "/file.rs",
9743 Point::new(2, 7),
9744 vec![
9745 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9746 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9747 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9748 ],
9749 )
9750 .await;
9751 editor
9752 .condition(&cx, |editor, _| editor.context_menu_visible())
9753 .await;
9754
9755 editor.update(cx, |editor, cx| {
9756 editor.handle_input(&Input("i".to_string()), cx);
9757 });
9758
9759 handle_completion_request(
9760 &mut fake_server,
9761 "/file.rs",
9762 Point::new(2, 8),
9763 vec![
9764 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9765 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9766 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9767 ],
9768 )
9769 .await;
9770 editor
9771 .condition(&cx, |editor, _| editor.context_menu_visible())
9772 .await;
9773
9774 let apply_additional_edits = editor.update(cx, |editor, cx| {
9775 let apply_additional_edits = editor
9776 .confirm_completion(&ConfirmCompletion::default(), cx)
9777 .unwrap();
9778 assert_eq!(
9779 editor.text(cx),
9780 "
9781 one.second_completion
9782 two sixth_completion
9783 three sixth_completion
9784 additional edit
9785 "
9786 .unindent()
9787 );
9788 apply_additional_edits
9789 });
9790 handle_resolve_completion_request(&mut fake_server, None).await;
9791 apply_additional_edits.await.unwrap();
9792
9793 async fn handle_completion_request(
9794 fake: &mut FakeLanguageServer,
9795 path: &'static str,
9796 position: Point,
9797 completions: Vec<(Range<Point>, &'static str)>,
9798 ) {
9799 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9800 let completions = completions.clone();
9801 async move {
9802 assert_eq!(
9803 params.text_document_position.text_document.uri,
9804 lsp::Url::from_file_path(path).unwrap()
9805 );
9806 assert_eq!(
9807 params.text_document_position.position,
9808 lsp::Position::new(position.row, position.column)
9809 );
9810 Ok(Some(lsp::CompletionResponse::Array(
9811 completions
9812 .iter()
9813 .map(|(range, new_text)| lsp::CompletionItem {
9814 label: new_text.to_string(),
9815 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9816 range: lsp::Range::new(
9817 lsp::Position::new(range.start.row, range.start.column),
9818 lsp::Position::new(range.start.row, range.start.column),
9819 ),
9820 new_text: new_text.to_string(),
9821 })),
9822 ..Default::default()
9823 })
9824 .collect(),
9825 )))
9826 }
9827 })
9828 .next()
9829 .await;
9830 }
9831
9832 async fn handle_resolve_completion_request(
9833 fake: &mut FakeLanguageServer,
9834 edit: Option<(Range<Point>, &'static str)>,
9835 ) {
9836 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9837 let edit = edit.clone();
9838 async move {
9839 Ok(lsp::CompletionItem {
9840 additional_text_edits: edit.map(|(range, new_text)| {
9841 vec![lsp::TextEdit::new(
9842 lsp::Range::new(
9843 lsp::Position::new(range.start.row, range.start.column),
9844 lsp::Position::new(range.end.row, range.end.column),
9845 ),
9846 new_text.to_string(),
9847 )]
9848 }),
9849 ..Default::default()
9850 })
9851 }
9852 })
9853 .next()
9854 .await;
9855 }
9856 }
9857
9858 #[gpui::test]
9859 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9860 cx.update(|cx| cx.set_global(Settings::test(cx)));
9861 let language = Arc::new(Language::new(
9862 LanguageConfig {
9863 line_comment: Some("// ".to_string()),
9864 ..Default::default()
9865 },
9866 Some(tree_sitter_rust::language()),
9867 ));
9868
9869 let text = "
9870 fn a() {
9871 //b();
9872 // c();
9873 // d();
9874 }
9875 "
9876 .unindent();
9877
9878 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9879 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9880 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9881
9882 view.update(cx, |editor, cx| {
9883 // If multiple selections intersect a line, the line is only
9884 // toggled once.
9885 editor.change_selections(None, cx, |s| {
9886 s.select_display_ranges([
9887 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9888 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9889 ])
9890 });
9891 editor.toggle_comments(&ToggleComments, cx);
9892 assert_eq!(
9893 editor.text(cx),
9894 "
9895 fn a() {
9896 b();
9897 c();
9898 d();
9899 }
9900 "
9901 .unindent()
9902 );
9903
9904 // The comment prefix is inserted at the same column for every line
9905 // in a selection.
9906 editor.change_selections(None, cx, |s| {
9907 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9908 });
9909 editor.toggle_comments(&ToggleComments, cx);
9910 assert_eq!(
9911 editor.text(cx),
9912 "
9913 fn a() {
9914 // b();
9915 // c();
9916 // d();
9917 }
9918 "
9919 .unindent()
9920 );
9921
9922 // If a selection ends at the beginning of a line, that line is not toggled.
9923 editor.change_selections(None, cx, |s| {
9924 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9925 });
9926 editor.toggle_comments(&ToggleComments, cx);
9927 assert_eq!(
9928 editor.text(cx),
9929 "
9930 fn a() {
9931 // b();
9932 c();
9933 // d();
9934 }
9935 "
9936 .unindent()
9937 );
9938 });
9939 }
9940
9941 #[gpui::test]
9942 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9943 cx.set_global(Settings::test(cx));
9944 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9945 let multibuffer = cx.add_model(|cx| {
9946 let mut multibuffer = MultiBuffer::new(0);
9947 multibuffer.push_excerpts(
9948 buffer.clone(),
9949 [
9950 ExcerptRange {
9951 context: Point::new(0, 0)..Point::new(0, 4),
9952 primary: None,
9953 },
9954 ExcerptRange {
9955 context: Point::new(1, 0)..Point::new(1, 4),
9956 primary: None,
9957 },
9958 ],
9959 cx,
9960 );
9961 multibuffer
9962 });
9963
9964 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9965
9966 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9967 view.update(cx, |view, cx| {
9968 assert_eq!(view.text(cx), "aaaa\nbbbb");
9969 view.change_selections(None, cx, |s| {
9970 s.select_ranges([
9971 Point::new(0, 0)..Point::new(0, 0),
9972 Point::new(1, 0)..Point::new(1, 0),
9973 ])
9974 });
9975
9976 view.handle_input(&Input("X".to_string()), cx);
9977 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9978 assert_eq!(
9979 view.selections.ranges(cx),
9980 [
9981 Point::new(0, 1)..Point::new(0, 1),
9982 Point::new(1, 1)..Point::new(1, 1),
9983 ]
9984 )
9985 });
9986 }
9987
9988 #[gpui::test]
9989 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9990 cx.set_global(Settings::test(cx));
9991 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9992 [aaaa
9993 (bbbb]
9994 cccc)"});
9995 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9996 context,
9997 primary: None,
9998 });
9999 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10000 let multibuffer = cx.add_model(|cx| {
10001 let mut multibuffer = MultiBuffer::new(0);
10002 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10003 multibuffer
10004 });
10005
10006 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10007 view.update(cx, |view, cx| {
10008 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10009 aaaa
10010 b|bbb
10011 b|bb|b
10012 cccc"});
10013 assert_eq!(view.text(cx), expected_text);
10014 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10015
10016 view.handle_input(&Input("X".to_string()), cx);
10017
10018 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10019 aaaa
10020 bX|bbXb
10021 bX|bbX|b
10022 cccc"});
10023 assert_eq!(view.text(cx), expected_text);
10024 assert_eq!(view.selections.ranges(cx), expected_selections);
10025
10026 view.newline(&Newline, cx);
10027 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10028 aaaa
10029 bX
10030 |bbX
10031 b
10032 bX
10033 |bbX
10034 |b
10035 cccc"});
10036 assert_eq!(view.text(cx), expected_text);
10037 assert_eq!(view.selections.ranges(cx), expected_selections);
10038 });
10039 }
10040
10041 #[gpui::test]
10042 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10043 cx.set_global(Settings::test(cx));
10044 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10045 let mut excerpt1_id = None;
10046 let multibuffer = cx.add_model(|cx| {
10047 let mut multibuffer = MultiBuffer::new(0);
10048 excerpt1_id = multibuffer
10049 .push_excerpts(
10050 buffer.clone(),
10051 [
10052 ExcerptRange {
10053 context: Point::new(0, 0)..Point::new(1, 4),
10054 primary: None,
10055 },
10056 ExcerptRange {
10057 context: Point::new(1, 0)..Point::new(2, 4),
10058 primary: None,
10059 },
10060 ],
10061 cx,
10062 )
10063 .into_iter()
10064 .next();
10065 multibuffer
10066 });
10067 assert_eq!(
10068 multibuffer.read(cx).read(cx).text(),
10069 "aaaa\nbbbb\nbbbb\ncccc"
10070 );
10071 let (_, editor) = cx.add_window(Default::default(), |cx| {
10072 let mut editor = build_editor(multibuffer.clone(), cx);
10073 let snapshot = editor.snapshot(cx);
10074 editor.change_selections(None, cx, |s| {
10075 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10076 });
10077 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10078 assert_eq!(
10079 editor.selections.ranges(cx),
10080 [
10081 Point::new(1, 3)..Point::new(1, 3),
10082 Point::new(2, 1)..Point::new(2, 1),
10083 ]
10084 );
10085 editor
10086 });
10087
10088 // Refreshing selections is a no-op when excerpts haven't changed.
10089 editor.update(cx, |editor, cx| {
10090 editor.change_selections(None, cx, |s| {
10091 s.refresh();
10092 });
10093 assert_eq!(
10094 editor.selections.ranges(cx),
10095 [
10096 Point::new(1, 3)..Point::new(1, 3),
10097 Point::new(2, 1)..Point::new(2, 1),
10098 ]
10099 );
10100 });
10101
10102 multibuffer.update(cx, |multibuffer, cx| {
10103 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10104 });
10105 editor.update(cx, |editor, cx| {
10106 // Removing an excerpt causes the first selection to become degenerate.
10107 assert_eq!(
10108 editor.selections.ranges(cx),
10109 [
10110 Point::new(0, 0)..Point::new(0, 0),
10111 Point::new(0, 1)..Point::new(0, 1)
10112 ]
10113 );
10114
10115 // Refreshing selections will relocate the first selection to the original buffer
10116 // location.
10117 editor.change_selections(None, cx, |s| {
10118 s.refresh();
10119 });
10120 assert_eq!(
10121 editor.selections.ranges(cx),
10122 [
10123 Point::new(0, 1)..Point::new(0, 1),
10124 Point::new(0, 3)..Point::new(0, 3)
10125 ]
10126 );
10127 assert!(editor.selections.pending_anchor().is_some());
10128 });
10129 }
10130
10131 #[gpui::test]
10132 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10133 cx.set_global(Settings::test(cx));
10134 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10135 let mut excerpt1_id = None;
10136 let multibuffer = cx.add_model(|cx| {
10137 let mut multibuffer = MultiBuffer::new(0);
10138 excerpt1_id = multibuffer
10139 .push_excerpts(
10140 buffer.clone(),
10141 [
10142 ExcerptRange {
10143 context: Point::new(0, 0)..Point::new(1, 4),
10144 primary: None,
10145 },
10146 ExcerptRange {
10147 context: Point::new(1, 0)..Point::new(2, 4),
10148 primary: None,
10149 },
10150 ],
10151 cx,
10152 )
10153 .into_iter()
10154 .next();
10155 multibuffer
10156 });
10157 assert_eq!(
10158 multibuffer.read(cx).read(cx).text(),
10159 "aaaa\nbbbb\nbbbb\ncccc"
10160 );
10161 let (_, editor) = cx.add_window(Default::default(), |cx| {
10162 let mut editor = build_editor(multibuffer.clone(), cx);
10163 let snapshot = editor.snapshot(cx);
10164 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10165 assert_eq!(
10166 editor.selections.ranges(cx),
10167 [Point::new(1, 3)..Point::new(1, 3)]
10168 );
10169 editor
10170 });
10171
10172 multibuffer.update(cx, |multibuffer, cx| {
10173 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10174 });
10175 editor.update(cx, |editor, cx| {
10176 assert_eq!(
10177 editor.selections.ranges(cx),
10178 [Point::new(0, 0)..Point::new(0, 0)]
10179 );
10180
10181 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10182 editor.change_selections(None, cx, |s| {
10183 s.refresh();
10184 });
10185 assert_eq!(
10186 editor.selections.ranges(cx),
10187 [Point::new(0, 3)..Point::new(0, 3)]
10188 );
10189 assert!(editor.selections.pending_anchor().is_some());
10190 });
10191 }
10192
10193 #[gpui::test]
10194 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10195 cx.update(|cx| cx.set_global(Settings::test(cx)));
10196 let language = Arc::new(
10197 Language::new(
10198 LanguageConfig {
10199 brackets: vec![
10200 BracketPair {
10201 start: "{".to_string(),
10202 end: "}".to_string(),
10203 close: true,
10204 newline: true,
10205 },
10206 BracketPair {
10207 start: "/* ".to_string(),
10208 end: " */".to_string(),
10209 close: true,
10210 newline: true,
10211 },
10212 ],
10213 ..Default::default()
10214 },
10215 Some(tree_sitter_rust::language()),
10216 )
10217 .with_indents_query("")
10218 .unwrap(),
10219 );
10220
10221 let text = concat!(
10222 "{ }\n", // Suppress rustfmt
10223 " x\n", //
10224 " /* */\n", //
10225 "x\n", //
10226 "{{} }\n", //
10227 );
10228
10229 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10230 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10231 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10232 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10233 .await;
10234
10235 view.update(cx, |view, cx| {
10236 view.change_selections(None, cx, |s| {
10237 s.select_display_ranges([
10238 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10239 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10240 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10241 ])
10242 });
10243 view.newline(&Newline, cx);
10244
10245 assert_eq!(
10246 view.buffer().read(cx).read(cx).text(),
10247 concat!(
10248 "{ \n", // Suppress rustfmt
10249 "\n", //
10250 "}\n", //
10251 " x\n", //
10252 " /* \n", //
10253 " \n", //
10254 " */\n", //
10255 "x\n", //
10256 "{{} \n", //
10257 "}\n", //
10258 )
10259 );
10260 });
10261 }
10262
10263 #[gpui::test]
10264 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10265 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10266
10267 cx.set_global(Settings::test(cx));
10268 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10269
10270 editor.update(cx, |editor, cx| {
10271 struct Type1;
10272 struct Type2;
10273
10274 let buffer = buffer.read(cx).snapshot(cx);
10275
10276 let anchor_range = |range: Range<Point>| {
10277 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10278 };
10279
10280 editor.highlight_background::<Type1>(
10281 vec![
10282 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10283 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10284 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10285 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10286 ],
10287 |_| Color::red(),
10288 cx,
10289 );
10290 editor.highlight_background::<Type2>(
10291 vec![
10292 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10293 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10294 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10295 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10296 ],
10297 |_| Color::green(),
10298 cx,
10299 );
10300
10301 let snapshot = editor.snapshot(cx);
10302 let mut highlighted_ranges = editor.background_highlights_in_range(
10303 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10304 &snapshot,
10305 cx.global::<Settings>().theme.as_ref(),
10306 );
10307 // Enforce a consistent ordering based on color without relying on the ordering of the
10308 // highlight's `TypeId` which is non-deterministic.
10309 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10310 assert_eq!(
10311 highlighted_ranges,
10312 &[
10313 (
10314 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10315 Color::green(),
10316 ),
10317 (
10318 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10319 Color::green(),
10320 ),
10321 (
10322 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10323 Color::red(),
10324 ),
10325 (
10326 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10327 Color::red(),
10328 ),
10329 ]
10330 );
10331 assert_eq!(
10332 editor.background_highlights_in_range(
10333 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10334 &snapshot,
10335 cx.global::<Settings>().theme.as_ref(),
10336 ),
10337 &[(
10338 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10339 Color::red(),
10340 )]
10341 );
10342 });
10343 }
10344
10345 #[gpui::test]
10346 fn test_following(cx: &mut gpui::MutableAppContext) {
10347 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10348
10349 cx.set_global(Settings::test(cx));
10350
10351 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10352 let (_, follower) = cx.add_window(
10353 WindowOptions {
10354 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10355 ..Default::default()
10356 },
10357 |cx| build_editor(buffer.clone(), cx),
10358 );
10359
10360 let pending_update = Rc::new(RefCell::new(None));
10361 follower.update(cx, {
10362 let update = pending_update.clone();
10363 |_, cx| {
10364 cx.subscribe(&leader, move |_, leader, event, cx| {
10365 leader
10366 .read(cx)
10367 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10368 })
10369 .detach();
10370 }
10371 });
10372
10373 // Update the selections only
10374 leader.update(cx, |leader, cx| {
10375 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10376 });
10377 follower.update(cx, |follower, cx| {
10378 follower
10379 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10380 .unwrap();
10381 });
10382 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10383
10384 // Update the scroll position only
10385 leader.update(cx, |leader, cx| {
10386 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10387 });
10388 follower.update(cx, |follower, cx| {
10389 follower
10390 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10391 .unwrap();
10392 });
10393 assert_eq!(
10394 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10395 vec2f(1.5, 3.5)
10396 );
10397
10398 // Update the selections and scroll position
10399 leader.update(cx, |leader, cx| {
10400 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10401 leader.request_autoscroll(Autoscroll::Newest, cx);
10402 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10403 });
10404 follower.update(cx, |follower, cx| {
10405 let initial_scroll_position = follower.scroll_position(cx);
10406 follower
10407 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10408 .unwrap();
10409 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10410 assert!(follower.autoscroll_request.is_some());
10411 });
10412 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10413
10414 // Creating a pending selection that precedes another selection
10415 leader.update(cx, |leader, cx| {
10416 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10417 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10418 });
10419 follower.update(cx, |follower, cx| {
10420 follower
10421 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10422 .unwrap();
10423 });
10424 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10425
10426 // Extend the pending selection so that it surrounds another selection
10427 leader.update(cx, |leader, cx| {
10428 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10429 });
10430 follower.update(cx, |follower, cx| {
10431 follower
10432 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10433 .unwrap();
10434 });
10435 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10436 }
10437
10438 #[test]
10439 fn test_combine_syntax_and_fuzzy_match_highlights() {
10440 let string = "abcdefghijklmnop";
10441 let syntax_ranges = [
10442 (
10443 0..3,
10444 HighlightStyle {
10445 color: Some(Color::red()),
10446 ..Default::default()
10447 },
10448 ),
10449 (
10450 4..8,
10451 HighlightStyle {
10452 color: Some(Color::green()),
10453 ..Default::default()
10454 },
10455 ),
10456 ];
10457 let match_indices = [4, 6, 7, 8];
10458 assert_eq!(
10459 combine_syntax_and_fuzzy_match_highlights(
10460 &string,
10461 Default::default(),
10462 syntax_ranges.into_iter(),
10463 &match_indices,
10464 ),
10465 &[
10466 (
10467 0..3,
10468 HighlightStyle {
10469 color: Some(Color::red()),
10470 ..Default::default()
10471 },
10472 ),
10473 (
10474 4..5,
10475 HighlightStyle {
10476 color: Some(Color::green()),
10477 weight: Some(fonts::Weight::BOLD),
10478 ..Default::default()
10479 },
10480 ),
10481 (
10482 5..6,
10483 HighlightStyle {
10484 color: Some(Color::green()),
10485 ..Default::default()
10486 },
10487 ),
10488 (
10489 6..8,
10490 HighlightStyle {
10491 color: Some(Color::green()),
10492 weight: Some(fonts::Weight::BOLD),
10493 ..Default::default()
10494 },
10495 ),
10496 (
10497 8..9,
10498 HighlightStyle {
10499 weight: Some(fonts::Weight::BOLD),
10500 ..Default::default()
10501 },
10502 ),
10503 ]
10504 );
10505 }
10506
10507 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10508 let point = DisplayPoint::new(row as u32, column as u32);
10509 point..point
10510 }
10511
10512 fn assert_selection_ranges(
10513 marked_text: &str,
10514 selection_marker_pairs: Vec<(char, char)>,
10515 view: &mut Editor,
10516 cx: &mut ViewContext<Editor>,
10517 ) {
10518 let snapshot = view.snapshot(cx).display_snapshot;
10519 let mut marker_chars = Vec::new();
10520 for (start, end) in selection_marker_pairs.iter() {
10521 marker_chars.push(*start);
10522 marker_chars.push(*end);
10523 }
10524 let (_, markers) = marked_text_by(marked_text, marker_chars);
10525 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10526 .iter()
10527 .map(|(start, end)| {
10528 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10529 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10530 start..end
10531 })
10532 .collect();
10533 assert_eq!(
10534 view.selections.display_ranges(cx),
10535 &asserted_ranges[..],
10536 "Assert selections are {}",
10537 marked_text
10538 );
10539 }
10540}
10541
10542trait RangeExt<T> {
10543 fn sorted(&self) -> Range<T>;
10544 fn to_inclusive(&self) -> RangeInclusive<T>;
10545}
10546
10547impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10548 fn sorted(&self) -> Self {
10549 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10550 }
10551
10552 fn to_inclusive(&self) -> RangeInclusive<T> {
10553 self.start.clone()..=self.end.clone()
10554 }
10555}