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