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