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