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