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