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