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