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