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