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