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