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