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