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