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