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