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