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