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