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