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