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