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 }
5953}
5954
5955fn build_style(
5956 settings: &Settings,
5957 get_field_editor_theme: Option<GetFieldEditorTheme>,
5958 override_text_style: Option<&OverrideTextStyle>,
5959 cx: &AppContext,
5960) -> EditorStyle {
5961 let font_cache = cx.font_cache();
5962
5963 let mut theme = settings.theme.editor.clone();
5964 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5965 let field_editor_theme = get_field_editor_theme(&settings.theme);
5966 theme.text_color = field_editor_theme.text.color;
5967 theme.selection = field_editor_theme.selection;
5968 theme.background = field_editor_theme
5969 .container
5970 .background_color
5971 .unwrap_or_default();
5972 EditorStyle {
5973 text: field_editor_theme.text,
5974 placeholder_text: field_editor_theme.placeholder_text,
5975 theme,
5976 }
5977 } else {
5978 let font_family_id = settings.buffer_font_family;
5979 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5980 let font_properties = Default::default();
5981 let font_id = font_cache
5982 .select_font(font_family_id, &font_properties)
5983 .unwrap();
5984 let font_size = settings.buffer_font_size;
5985 EditorStyle {
5986 text: TextStyle {
5987 color: settings.theme.editor.text_color,
5988 font_family_name,
5989 font_family_id,
5990 font_id,
5991 font_size,
5992 font_properties,
5993 underline: Default::default(),
5994 },
5995 placeholder_text: None,
5996 theme,
5997 }
5998 };
5999
6000 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6001 if let Some(highlighted) = style
6002 .text
6003 .clone()
6004 .highlight(highlight_style, font_cache)
6005 .log_err()
6006 {
6007 style.text = highlighted;
6008 }
6009 }
6010
6011 style
6012}
6013
6014trait SelectionExt {
6015 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6016 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6017 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6018 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6019 -> Range<u32>;
6020}
6021
6022impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6023 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6024 let start = self.start.to_point(buffer);
6025 let end = self.end.to_point(buffer);
6026 if self.reversed {
6027 end..start
6028 } else {
6029 start..end
6030 }
6031 }
6032
6033 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6034 let start = self.start.to_offset(buffer);
6035 let end = self.end.to_offset(buffer);
6036 if self.reversed {
6037 end..start
6038 } else {
6039 start..end
6040 }
6041 }
6042
6043 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6044 let start = self
6045 .start
6046 .to_point(&map.buffer_snapshot)
6047 .to_display_point(map);
6048 let end = self
6049 .end
6050 .to_point(&map.buffer_snapshot)
6051 .to_display_point(map);
6052 if self.reversed {
6053 end..start
6054 } else {
6055 start..end
6056 }
6057 }
6058
6059 fn spanned_rows(
6060 &self,
6061 include_end_if_at_line_start: bool,
6062 map: &DisplaySnapshot,
6063 ) -> Range<u32> {
6064 let start = self.start.to_point(&map.buffer_snapshot);
6065 let mut end = self.end.to_point(&map.buffer_snapshot);
6066 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6067 end.row -= 1;
6068 }
6069
6070 let buffer_start = map.prev_line_boundary(start).0;
6071 let buffer_end = map.next_line_boundary(end).0;
6072 buffer_start.row..buffer_end.row + 1
6073 }
6074}
6075
6076impl<T: InvalidationRegion> InvalidationStack<T> {
6077 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6078 where
6079 S: Clone + ToOffset,
6080 {
6081 while let Some(region) = self.last() {
6082 let all_selections_inside_invalidation_ranges =
6083 if selections.len() == region.ranges().len() {
6084 selections
6085 .iter()
6086 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6087 .all(|(selection, invalidation_range)| {
6088 let head = selection.head().to_offset(&buffer);
6089 invalidation_range.start <= head && invalidation_range.end >= head
6090 })
6091 } else {
6092 false
6093 };
6094
6095 if all_selections_inside_invalidation_ranges {
6096 break;
6097 } else {
6098 self.pop();
6099 }
6100 }
6101 }
6102}
6103
6104impl<T> Default for InvalidationStack<T> {
6105 fn default() -> Self {
6106 Self(Default::default())
6107 }
6108}
6109
6110impl<T> Deref for InvalidationStack<T> {
6111 type Target = Vec<T>;
6112
6113 fn deref(&self) -> &Self::Target {
6114 &self.0
6115 }
6116}
6117
6118impl<T> DerefMut for InvalidationStack<T> {
6119 fn deref_mut(&mut self) -> &mut Self::Target {
6120 &mut self.0
6121 }
6122}
6123
6124impl InvalidationRegion for BracketPairState {
6125 fn ranges(&self) -> &[Range<Anchor>] {
6126 &self.ranges
6127 }
6128}
6129
6130impl InvalidationRegion for SnippetState {
6131 fn ranges(&self) -> &[Range<Anchor>] {
6132 &self.ranges[self.active_index]
6133 }
6134}
6135
6136impl Deref for EditorStyle {
6137 type Target = theme::Editor;
6138
6139 fn deref(&self) -> &Self::Target {
6140 &self.theme
6141 }
6142}
6143
6144pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6145 let mut highlighted_lines = Vec::new();
6146 for line in diagnostic.message.lines() {
6147 highlighted_lines.push(highlight_diagnostic_message(line));
6148 }
6149
6150 Arc::new(move |cx: &mut BlockContext| {
6151 let settings = cx.global::<Settings>();
6152 let theme = &settings.theme.editor;
6153 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6154 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6155 Flex::column()
6156 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6157 Label::new(
6158 line.clone(),
6159 style.message.clone().with_font_size(font_size),
6160 )
6161 .with_highlights(highlights.clone())
6162 .contained()
6163 .with_margin_left(cx.anchor_x)
6164 .boxed()
6165 }))
6166 .aligned()
6167 .left()
6168 .boxed()
6169 })
6170}
6171
6172pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6173 let mut message_without_backticks = String::new();
6174 let mut prev_offset = 0;
6175 let mut inside_block = false;
6176 let mut highlights = Vec::new();
6177 for (match_ix, (offset, _)) in message
6178 .match_indices('`')
6179 .chain([(message.len(), "")])
6180 .enumerate()
6181 {
6182 message_without_backticks.push_str(&message[prev_offset..offset]);
6183 if inside_block {
6184 highlights.extend(prev_offset - match_ix..offset - match_ix);
6185 }
6186
6187 inside_block = !inside_block;
6188 prev_offset = offset + 1;
6189 }
6190
6191 (message_without_backticks, highlights)
6192}
6193
6194pub fn diagnostic_style(
6195 severity: DiagnosticSeverity,
6196 valid: bool,
6197 theme: &theme::Editor,
6198) -> DiagnosticStyle {
6199 match (severity, valid) {
6200 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6201 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6202 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6203 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6204 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6205 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6206 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6207 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6208 _ => theme.invalid_hint_diagnostic.clone(),
6209 }
6210}
6211
6212pub fn combine_syntax_and_fuzzy_match_highlights(
6213 text: &str,
6214 default_style: HighlightStyle,
6215 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6216 match_indices: &[usize],
6217) -> Vec<(Range<usize>, HighlightStyle)> {
6218 let mut result = Vec::new();
6219 let mut match_indices = match_indices.iter().copied().peekable();
6220
6221 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6222 {
6223 syntax_highlight.weight = None;
6224
6225 // Add highlights for any fuzzy match characters before the next
6226 // syntax highlight range.
6227 while let Some(&match_index) = match_indices.peek() {
6228 if match_index >= range.start {
6229 break;
6230 }
6231 match_indices.next();
6232 let end_index = char_ix_after(match_index, text);
6233 let mut match_style = default_style;
6234 match_style.weight = Some(fonts::Weight::BOLD);
6235 result.push((match_index..end_index, match_style));
6236 }
6237
6238 if range.start == usize::MAX {
6239 break;
6240 }
6241
6242 // Add highlights for any fuzzy match characters within the
6243 // syntax highlight range.
6244 let mut offset = range.start;
6245 while let Some(&match_index) = match_indices.peek() {
6246 if match_index >= range.end {
6247 break;
6248 }
6249
6250 match_indices.next();
6251 if match_index > offset {
6252 result.push((offset..match_index, syntax_highlight));
6253 }
6254
6255 let mut end_index = char_ix_after(match_index, text);
6256 while let Some(&next_match_index) = match_indices.peek() {
6257 if next_match_index == end_index && next_match_index < range.end {
6258 end_index = char_ix_after(next_match_index, text);
6259 match_indices.next();
6260 } else {
6261 break;
6262 }
6263 }
6264
6265 let mut match_style = syntax_highlight;
6266 match_style.weight = Some(fonts::Weight::BOLD);
6267 result.push((match_index..end_index, match_style));
6268 offset = end_index;
6269 }
6270
6271 if offset < range.end {
6272 result.push((offset..range.end, syntax_highlight));
6273 }
6274 }
6275
6276 fn char_ix_after(ix: usize, text: &str) -> usize {
6277 ix + text[ix..].chars().next().unwrap().len_utf8()
6278 }
6279
6280 result
6281}
6282
6283pub fn styled_runs_for_code_label<'a>(
6284 label: &'a CodeLabel,
6285 syntax_theme: &'a theme::SyntaxTheme,
6286) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6287 let fade_out = HighlightStyle {
6288 fade_out: Some(0.35),
6289 ..Default::default()
6290 };
6291
6292 let mut prev_end = label.filter_range.end;
6293 label
6294 .runs
6295 .iter()
6296 .enumerate()
6297 .flat_map(move |(ix, (range, highlight_id))| {
6298 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6299 style
6300 } else {
6301 return Default::default();
6302 };
6303 let mut muted_style = style.clone();
6304 muted_style.highlight(fade_out);
6305
6306 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6307 if range.start >= label.filter_range.end {
6308 if range.start > prev_end {
6309 runs.push((prev_end..range.start, fade_out));
6310 }
6311 runs.push((range.clone(), muted_style));
6312 } else if range.end <= label.filter_range.end {
6313 runs.push((range.clone(), style));
6314 } else {
6315 runs.push((range.start..label.filter_range.end, style));
6316 runs.push((label.filter_range.end..range.end, muted_style));
6317 }
6318 prev_end = cmp::max(prev_end, range.end);
6319
6320 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6321 runs.push((prev_end..label.text.len(), fade_out));
6322 }
6323
6324 runs
6325 })
6326}
6327
6328#[cfg(test)]
6329mod tests {
6330 use crate::test::{
6331 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6332 EditorTestContext,
6333 };
6334
6335 use super::*;
6336 use futures::StreamExt;
6337 use gpui::{
6338 geometry::rect::RectF,
6339 platform::{WindowBounds, WindowOptions},
6340 };
6341 use indoc::indoc;
6342 use language::{FakeLspAdapter, LanguageConfig};
6343 use project::FakeFs;
6344 use settings::EditorSettings;
6345 use std::{cell::RefCell, rc::Rc, time::Instant};
6346 use text::Point;
6347 use unindent::Unindent;
6348 use util::{
6349 assert_set_eq,
6350 test::{
6351 marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker,
6352 },
6353 };
6354 use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane};
6355
6356 #[gpui::test]
6357 fn test_edit_events(cx: &mut MutableAppContext) {
6358 cx.set_global(Settings::test(cx));
6359 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6360
6361 let events = Rc::new(RefCell::new(Vec::new()));
6362 let (_, editor1) = cx.add_window(Default::default(), {
6363 let events = events.clone();
6364 |cx| {
6365 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6366 if matches!(
6367 event,
6368 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6369 ) {
6370 events.borrow_mut().push(("editor1", *event));
6371 }
6372 })
6373 .detach();
6374 Editor::for_buffer(buffer.clone(), None, cx)
6375 }
6376 });
6377 let (_, editor2) = cx.add_window(Default::default(), {
6378 let events = events.clone();
6379 |cx| {
6380 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6381 if matches!(
6382 event,
6383 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6384 ) {
6385 events.borrow_mut().push(("editor2", *event));
6386 }
6387 })
6388 .detach();
6389 Editor::for_buffer(buffer.clone(), None, cx)
6390 }
6391 });
6392 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6393
6394 // Mutating editor 1 will emit an `Edited` event only for that editor.
6395 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6396 assert_eq!(
6397 mem::take(&mut *events.borrow_mut()),
6398 [
6399 ("editor1", Event::Edited),
6400 ("editor1", Event::BufferEdited),
6401 ("editor2", Event::BufferEdited),
6402 ("editor1", Event::DirtyChanged),
6403 ("editor2", Event::DirtyChanged)
6404 ]
6405 );
6406
6407 // Mutating editor 2 will emit an `Edited` event only for that editor.
6408 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6409 assert_eq!(
6410 mem::take(&mut *events.borrow_mut()),
6411 [
6412 ("editor2", Event::Edited),
6413 ("editor1", Event::BufferEdited),
6414 ("editor2", Event::BufferEdited),
6415 ]
6416 );
6417
6418 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6419 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6420 assert_eq!(
6421 mem::take(&mut *events.borrow_mut()),
6422 [
6423 ("editor1", Event::Edited),
6424 ("editor1", Event::BufferEdited),
6425 ("editor2", Event::BufferEdited),
6426 ("editor1", Event::DirtyChanged),
6427 ("editor2", Event::DirtyChanged),
6428 ]
6429 );
6430
6431 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6432 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6433 assert_eq!(
6434 mem::take(&mut *events.borrow_mut()),
6435 [
6436 ("editor1", Event::Edited),
6437 ("editor1", Event::BufferEdited),
6438 ("editor2", Event::BufferEdited),
6439 ("editor1", Event::DirtyChanged),
6440 ("editor2", Event::DirtyChanged),
6441 ]
6442 );
6443
6444 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6445 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6446 assert_eq!(
6447 mem::take(&mut *events.borrow_mut()),
6448 [
6449 ("editor2", Event::Edited),
6450 ("editor1", Event::BufferEdited),
6451 ("editor2", Event::BufferEdited),
6452 ("editor1", Event::DirtyChanged),
6453 ("editor2", Event::DirtyChanged),
6454 ]
6455 );
6456
6457 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6458 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6459 assert_eq!(
6460 mem::take(&mut *events.borrow_mut()),
6461 [
6462 ("editor2", Event::Edited),
6463 ("editor1", Event::BufferEdited),
6464 ("editor2", Event::BufferEdited),
6465 ("editor1", Event::DirtyChanged),
6466 ("editor2", Event::DirtyChanged),
6467 ]
6468 );
6469
6470 // No event is emitted when the mutation is a no-op.
6471 editor2.update(cx, |editor, cx| {
6472 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6473
6474 editor.backspace(&Backspace, cx);
6475 });
6476 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6477 }
6478
6479 #[gpui::test]
6480 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6481 cx.set_global(Settings::test(cx));
6482 let mut now = Instant::now();
6483 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6484 let group_interval = buffer.read(cx).transaction_group_interval();
6485 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6486 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6487
6488 editor.update(cx, |editor, cx| {
6489 editor.start_transaction_at(now, cx);
6490 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6491
6492 editor.insert("cd", cx);
6493 editor.end_transaction_at(now, cx);
6494 assert_eq!(editor.text(cx), "12cd56");
6495 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6496
6497 editor.start_transaction_at(now, cx);
6498 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6499 editor.insert("e", cx);
6500 editor.end_transaction_at(now, cx);
6501 assert_eq!(editor.text(cx), "12cde6");
6502 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6503
6504 now += group_interval + Duration::from_millis(1);
6505 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6506
6507 // Simulate an edit in another editor
6508 buffer.update(cx, |buffer, cx| {
6509 buffer.start_transaction_at(now, cx);
6510 buffer.edit([(0..1, "a")], cx);
6511 buffer.edit([(1..1, "b")], cx);
6512 buffer.end_transaction_at(now, cx);
6513 });
6514
6515 assert_eq!(editor.text(cx), "ab2cde6");
6516 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6517
6518 // Last transaction happened past the group interval in a different editor.
6519 // Undo it individually and don't restore selections.
6520 editor.undo(&Undo, cx);
6521 assert_eq!(editor.text(cx), "12cde6");
6522 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6523
6524 // First two transactions happened within the group interval in this editor.
6525 // Undo them together and restore selections.
6526 editor.undo(&Undo, cx);
6527 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6528 assert_eq!(editor.text(cx), "123456");
6529 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6530
6531 // Redo the first two transactions together.
6532 editor.redo(&Redo, cx);
6533 assert_eq!(editor.text(cx), "12cde6");
6534 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6535
6536 // Redo the last transaction on its own.
6537 editor.redo(&Redo, cx);
6538 assert_eq!(editor.text(cx), "ab2cde6");
6539 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6540
6541 // Test empty transactions.
6542 editor.start_transaction_at(now, cx);
6543 editor.end_transaction_at(now, cx);
6544 editor.undo(&Undo, cx);
6545 assert_eq!(editor.text(cx), "12cde6");
6546 });
6547 }
6548
6549 #[gpui::test]
6550 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6551 cx.set_global(Settings::test(cx));
6552
6553 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6554 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6555 editor.update(cx, |view, cx| {
6556 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6557 });
6558 assert_eq!(
6559 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6560 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6561 );
6562
6563 editor.update(cx, |view, cx| {
6564 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6565 });
6566
6567 assert_eq!(
6568 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6569 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6570 );
6571
6572 editor.update(cx, |view, cx| {
6573 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6574 });
6575
6576 assert_eq!(
6577 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6578 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6579 );
6580
6581 editor.update(cx, |view, cx| {
6582 view.end_selection(cx);
6583 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6584 });
6585
6586 assert_eq!(
6587 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6588 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6589 );
6590
6591 editor.update(cx, |view, cx| {
6592 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6593 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6594 });
6595
6596 assert_eq!(
6597 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6598 [
6599 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6600 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6601 ]
6602 );
6603
6604 editor.update(cx, |view, cx| {
6605 view.end_selection(cx);
6606 });
6607
6608 assert_eq!(
6609 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6610 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6611 );
6612 }
6613
6614 #[gpui::test]
6615 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6616 cx.set_global(Settings::test(cx));
6617 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6618 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6619
6620 view.update(cx, |view, cx| {
6621 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6622 assert_eq!(
6623 view.selections.display_ranges(cx),
6624 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6625 );
6626 });
6627
6628 view.update(cx, |view, cx| {
6629 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6630 assert_eq!(
6631 view.selections.display_ranges(cx),
6632 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6633 );
6634 });
6635
6636 view.update(cx, |view, cx| {
6637 view.cancel(&Cancel, cx);
6638 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6639 assert_eq!(
6640 view.selections.display_ranges(cx),
6641 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6642 );
6643 });
6644 }
6645
6646 #[gpui::test]
6647 fn test_clone(cx: &mut gpui::MutableAppContext) {
6648 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6649 one
6650 two
6651 three[]
6652 four
6653 five[]
6654 "});
6655 cx.set_global(Settings::test(cx));
6656 let buffer = MultiBuffer::build_simple(&text, cx);
6657
6658 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6659
6660 editor.update(cx, |editor, cx| {
6661 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6662 editor.fold_ranges(
6663 [
6664 Point::new(1, 0)..Point::new(2, 0),
6665 Point::new(3, 0)..Point::new(4, 0),
6666 ],
6667 cx,
6668 );
6669 });
6670
6671 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6672 cx.add_window(Default::default(), |cx| editor.clone(cx))
6673 });
6674
6675 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6676 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6677
6678 assert_eq!(
6679 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6680 editor.update(cx, |e, cx| e.display_text(cx))
6681 );
6682 assert_eq!(
6683 cloned_snapshot
6684 .folds_in_range(0..text.len())
6685 .collect::<Vec<_>>(),
6686 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6687 );
6688 assert_set_eq!(
6689 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6690 editor.read(cx).selections.ranges(cx)
6691 );
6692 assert_set_eq!(
6693 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
6694 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
6695 );
6696 }
6697
6698 #[gpui::test]
6699 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6700 cx.set_global(Settings::test(cx));
6701 use workspace::Item;
6702 let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
6703 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6704
6705 cx.add_window(Default::default(), |cx| {
6706 let mut editor = build_editor(buffer.clone(), cx);
6707 let handle = cx.handle();
6708 editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
6709
6710 fn pop_history(
6711 editor: &mut Editor,
6712 cx: &mut MutableAppContext,
6713 ) -> Option<NavigationEntry> {
6714 editor.nav_history.as_mut().unwrap().pop_backward(cx)
6715 }
6716
6717 // Move the cursor a small distance.
6718 // Nothing is added to the navigation history.
6719 editor.change_selections(None, cx, |s| {
6720 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6721 });
6722 editor.change_selections(None, cx, |s| {
6723 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6724 });
6725 assert!(pop_history(&mut editor, cx).is_none());
6726
6727 // Move the cursor a large distance.
6728 // The history can jump back to the previous position.
6729 editor.change_selections(None, cx, |s| {
6730 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6731 });
6732 let nav_entry = pop_history(&mut editor, cx).unwrap();
6733 editor.navigate(nav_entry.data.unwrap(), cx);
6734 assert_eq!(nav_entry.item.id(), cx.view_id());
6735 assert_eq!(
6736 editor.selections.display_ranges(cx),
6737 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6738 );
6739 assert!(pop_history(&mut editor, cx).is_none());
6740
6741 // Move the cursor a small distance via the mouse.
6742 // Nothing is added to the navigation history.
6743 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6744 editor.end_selection(cx);
6745 assert_eq!(
6746 editor.selections.display_ranges(cx),
6747 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6748 );
6749 assert!(pop_history(&mut editor, cx).is_none());
6750
6751 // Move the cursor a large distance via the mouse.
6752 // The history can jump back to the previous position.
6753 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6754 editor.end_selection(cx);
6755 assert_eq!(
6756 editor.selections.display_ranges(cx),
6757 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6758 );
6759 let nav_entry = pop_history(&mut editor, cx).unwrap();
6760 editor.navigate(nav_entry.data.unwrap(), cx);
6761 assert_eq!(nav_entry.item.id(), cx.view_id());
6762 assert_eq!(
6763 editor.selections.display_ranges(cx),
6764 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6765 );
6766 assert!(pop_history(&mut editor, cx).is_none());
6767
6768 // Set scroll position to check later
6769 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6770 let original_scroll_position = editor.scroll_position;
6771 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6772
6773 // Jump to the end of the document and adjust scroll
6774 editor.move_to_end(&MoveToEnd, cx);
6775 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6776 assert_ne!(editor.scroll_position, original_scroll_position);
6777 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6778
6779 let nav_entry = pop_history(&mut editor, cx).unwrap();
6780 editor.navigate(nav_entry.data.unwrap(), cx);
6781 assert_eq!(editor.scroll_position, original_scroll_position);
6782 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6783
6784 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6785 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6786 invalid_anchor.text_anchor.buffer_id = Some(999);
6787 let invalid_point = Point::new(9999, 0);
6788 editor.navigate(
6789 Box::new(NavigationData {
6790 cursor_anchor: invalid_anchor.clone(),
6791 cursor_position: invalid_point,
6792 scroll_top_anchor: invalid_anchor.clone(),
6793 scroll_top_row: invalid_point.row,
6794 scroll_position: Default::default(),
6795 }),
6796 cx,
6797 );
6798 assert_eq!(
6799 editor.selections.display_ranges(cx),
6800 &[editor.max_point(cx)..editor.max_point(cx)]
6801 );
6802 assert_eq!(
6803 editor.scroll_position(cx),
6804 vec2f(0., editor.max_point(cx).row() as f32)
6805 );
6806
6807 editor
6808 });
6809 }
6810
6811 #[gpui::test]
6812 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6813 cx.set_global(Settings::test(cx));
6814 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6815 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6816
6817 view.update(cx, |view, cx| {
6818 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6819 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6820 view.end_selection(cx);
6821
6822 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6823 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6824 view.end_selection(cx);
6825 assert_eq!(
6826 view.selections.display_ranges(cx),
6827 [
6828 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6829 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6830 ]
6831 );
6832 });
6833
6834 view.update(cx, |view, cx| {
6835 view.cancel(&Cancel, cx);
6836 assert_eq!(
6837 view.selections.display_ranges(cx),
6838 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6839 );
6840 });
6841
6842 view.update(cx, |view, cx| {
6843 view.cancel(&Cancel, cx);
6844 assert_eq!(
6845 view.selections.display_ranges(cx),
6846 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6847 );
6848 });
6849 }
6850
6851 #[gpui::test]
6852 fn test_fold(cx: &mut gpui::MutableAppContext) {
6853 cx.set_global(Settings::test(cx));
6854 let buffer = MultiBuffer::build_simple(
6855 &"
6856 impl Foo {
6857 // Hello!
6858
6859 fn a() {
6860 1
6861 }
6862
6863 fn b() {
6864 2
6865 }
6866
6867 fn c() {
6868 3
6869 }
6870 }
6871 "
6872 .unindent(),
6873 cx,
6874 );
6875 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6876
6877 view.update(cx, |view, cx| {
6878 view.change_selections(None, cx, |s| {
6879 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6880 });
6881 view.fold(&Fold, cx);
6882 assert_eq!(
6883 view.display_text(cx),
6884 "
6885 impl Foo {
6886 // Hello!
6887
6888 fn a() {
6889 1
6890 }
6891
6892 fn b() {…
6893 }
6894
6895 fn c() {…
6896 }
6897 }
6898 "
6899 .unindent(),
6900 );
6901
6902 view.fold(&Fold, cx);
6903 assert_eq!(
6904 view.display_text(cx),
6905 "
6906 impl Foo {…
6907 }
6908 "
6909 .unindent(),
6910 );
6911
6912 view.unfold_lines(&UnfoldLines, cx);
6913 assert_eq!(
6914 view.display_text(cx),
6915 "
6916 impl Foo {
6917 // Hello!
6918
6919 fn a() {
6920 1
6921 }
6922
6923 fn b() {…
6924 }
6925
6926 fn c() {…
6927 }
6928 }
6929 "
6930 .unindent(),
6931 );
6932
6933 view.unfold_lines(&UnfoldLines, cx);
6934 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6935 });
6936 }
6937
6938 #[gpui::test]
6939 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6940 cx.set_global(Settings::test(cx));
6941 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6942 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6943
6944 buffer.update(cx, |buffer, cx| {
6945 buffer.edit(
6946 vec![
6947 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6948 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6949 ],
6950 cx,
6951 );
6952 });
6953
6954 view.update(cx, |view, cx| {
6955 assert_eq!(
6956 view.selections.display_ranges(cx),
6957 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6958 );
6959
6960 view.move_down(&MoveDown, cx);
6961 assert_eq!(
6962 view.selections.display_ranges(cx),
6963 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6964 );
6965
6966 view.move_right(&MoveRight, cx);
6967 assert_eq!(
6968 view.selections.display_ranges(cx),
6969 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6970 );
6971
6972 view.move_left(&MoveLeft, cx);
6973 assert_eq!(
6974 view.selections.display_ranges(cx),
6975 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6976 );
6977
6978 view.move_up(&MoveUp, cx);
6979 assert_eq!(
6980 view.selections.display_ranges(cx),
6981 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6982 );
6983
6984 view.move_to_end(&MoveToEnd, cx);
6985 assert_eq!(
6986 view.selections.display_ranges(cx),
6987 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6988 );
6989
6990 view.move_to_beginning(&MoveToBeginning, cx);
6991 assert_eq!(
6992 view.selections.display_ranges(cx),
6993 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6994 );
6995
6996 view.change_selections(None, cx, |s| {
6997 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6998 });
6999 view.select_to_beginning(&SelectToBeginning, cx);
7000 assert_eq!(
7001 view.selections.display_ranges(cx),
7002 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7003 );
7004
7005 view.select_to_end(&SelectToEnd, cx);
7006 assert_eq!(
7007 view.selections.display_ranges(cx),
7008 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7009 );
7010 });
7011 }
7012
7013 #[gpui::test]
7014 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7015 cx.set_global(Settings::test(cx));
7016 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7017 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7018
7019 assert_eq!('ⓐ'.len_utf8(), 3);
7020 assert_eq!('α'.len_utf8(), 2);
7021
7022 view.update(cx, |view, cx| {
7023 view.fold_ranges(
7024 vec![
7025 Point::new(0, 6)..Point::new(0, 12),
7026 Point::new(1, 2)..Point::new(1, 4),
7027 Point::new(2, 4)..Point::new(2, 8),
7028 ],
7029 cx,
7030 );
7031 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7032
7033 view.move_right(&MoveRight, cx);
7034 assert_eq!(
7035 view.selections.display_ranges(cx),
7036 &[empty_range(0, "ⓐ".len())]
7037 );
7038 view.move_right(&MoveRight, cx);
7039 assert_eq!(
7040 view.selections.display_ranges(cx),
7041 &[empty_range(0, "ⓐⓑ".len())]
7042 );
7043 view.move_right(&MoveRight, cx);
7044 assert_eq!(
7045 view.selections.display_ranges(cx),
7046 &[empty_range(0, "ⓐⓑ…".len())]
7047 );
7048
7049 view.move_down(&MoveDown, cx);
7050 assert_eq!(
7051 view.selections.display_ranges(cx),
7052 &[empty_range(1, "ab…".len())]
7053 );
7054 view.move_left(&MoveLeft, cx);
7055 assert_eq!(
7056 view.selections.display_ranges(cx),
7057 &[empty_range(1, "ab".len())]
7058 );
7059 view.move_left(&MoveLeft, cx);
7060 assert_eq!(
7061 view.selections.display_ranges(cx),
7062 &[empty_range(1, "a".len())]
7063 );
7064
7065 view.move_down(&MoveDown, cx);
7066 assert_eq!(
7067 view.selections.display_ranges(cx),
7068 &[empty_range(2, "α".len())]
7069 );
7070 view.move_right(&MoveRight, cx);
7071 assert_eq!(
7072 view.selections.display_ranges(cx),
7073 &[empty_range(2, "αβ".len())]
7074 );
7075 view.move_right(&MoveRight, cx);
7076 assert_eq!(
7077 view.selections.display_ranges(cx),
7078 &[empty_range(2, "αβ…".len())]
7079 );
7080 view.move_right(&MoveRight, cx);
7081 assert_eq!(
7082 view.selections.display_ranges(cx),
7083 &[empty_range(2, "αβ…ε".len())]
7084 );
7085
7086 view.move_up(&MoveUp, cx);
7087 assert_eq!(
7088 view.selections.display_ranges(cx),
7089 &[empty_range(1, "ab…e".len())]
7090 );
7091 view.move_up(&MoveUp, cx);
7092 assert_eq!(
7093 view.selections.display_ranges(cx),
7094 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7095 );
7096 view.move_left(&MoveLeft, cx);
7097 assert_eq!(
7098 view.selections.display_ranges(cx),
7099 &[empty_range(0, "ⓐⓑ…".len())]
7100 );
7101 view.move_left(&MoveLeft, cx);
7102 assert_eq!(
7103 view.selections.display_ranges(cx),
7104 &[empty_range(0, "ⓐⓑ".len())]
7105 );
7106 view.move_left(&MoveLeft, cx);
7107 assert_eq!(
7108 view.selections.display_ranges(cx),
7109 &[empty_range(0, "ⓐ".len())]
7110 );
7111 });
7112 }
7113
7114 #[gpui::test]
7115 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7116 cx.set_global(Settings::test(cx));
7117 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7118 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7119 view.update(cx, |view, cx| {
7120 view.change_selections(None, cx, |s| {
7121 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7122 });
7123 view.move_down(&MoveDown, cx);
7124 assert_eq!(
7125 view.selections.display_ranges(cx),
7126 &[empty_range(1, "abcd".len())]
7127 );
7128
7129 view.move_down(&MoveDown, cx);
7130 assert_eq!(
7131 view.selections.display_ranges(cx),
7132 &[empty_range(2, "αβγ".len())]
7133 );
7134
7135 view.move_down(&MoveDown, cx);
7136 assert_eq!(
7137 view.selections.display_ranges(cx),
7138 &[empty_range(3, "abcd".len())]
7139 );
7140
7141 view.move_down(&MoveDown, cx);
7142 assert_eq!(
7143 view.selections.display_ranges(cx),
7144 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7145 );
7146
7147 view.move_up(&MoveUp, cx);
7148 assert_eq!(
7149 view.selections.display_ranges(cx),
7150 &[empty_range(3, "abcd".len())]
7151 );
7152
7153 view.move_up(&MoveUp, cx);
7154 assert_eq!(
7155 view.selections.display_ranges(cx),
7156 &[empty_range(2, "αβγ".len())]
7157 );
7158 });
7159 }
7160
7161 #[gpui::test]
7162 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7163 cx.set_global(Settings::test(cx));
7164 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7165 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7166 view.update(cx, |view, cx| {
7167 view.change_selections(None, cx, |s| {
7168 s.select_display_ranges([
7169 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7170 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7171 ]);
7172 });
7173 });
7174
7175 view.update(cx, |view, cx| {
7176 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7177 assert_eq!(
7178 view.selections.display_ranges(cx),
7179 &[
7180 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7181 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7182 ]
7183 );
7184 });
7185
7186 view.update(cx, |view, cx| {
7187 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7188 assert_eq!(
7189 view.selections.display_ranges(cx),
7190 &[
7191 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7192 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7193 ]
7194 );
7195 });
7196
7197 view.update(cx, |view, cx| {
7198 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7199 assert_eq!(
7200 view.selections.display_ranges(cx),
7201 &[
7202 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7203 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7204 ]
7205 );
7206 });
7207
7208 view.update(cx, |view, cx| {
7209 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7210 assert_eq!(
7211 view.selections.display_ranges(cx),
7212 &[
7213 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7214 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7215 ]
7216 );
7217 });
7218
7219 // Moving to the end of line again is a no-op.
7220 view.update(cx, |view, cx| {
7221 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7222 assert_eq!(
7223 view.selections.display_ranges(cx),
7224 &[
7225 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7226 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7227 ]
7228 );
7229 });
7230
7231 view.update(cx, |view, cx| {
7232 view.move_left(&MoveLeft, cx);
7233 view.select_to_beginning_of_line(
7234 &SelectToBeginningOfLine {
7235 stop_at_soft_wraps: true,
7236 },
7237 cx,
7238 );
7239 assert_eq!(
7240 view.selections.display_ranges(cx),
7241 &[
7242 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7243 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7244 ]
7245 );
7246 });
7247
7248 view.update(cx, |view, cx| {
7249 view.select_to_beginning_of_line(
7250 &SelectToBeginningOfLine {
7251 stop_at_soft_wraps: true,
7252 },
7253 cx,
7254 );
7255 assert_eq!(
7256 view.selections.display_ranges(cx),
7257 &[
7258 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7259 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7260 ]
7261 );
7262 });
7263
7264 view.update(cx, |view, cx| {
7265 view.select_to_beginning_of_line(
7266 &SelectToBeginningOfLine {
7267 stop_at_soft_wraps: true,
7268 },
7269 cx,
7270 );
7271 assert_eq!(
7272 view.selections.display_ranges(cx),
7273 &[
7274 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7275 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7276 ]
7277 );
7278 });
7279
7280 view.update(cx, |view, cx| {
7281 view.select_to_end_of_line(
7282 &SelectToEndOfLine {
7283 stop_at_soft_wraps: true,
7284 },
7285 cx,
7286 );
7287 assert_eq!(
7288 view.selections.display_ranges(cx),
7289 &[
7290 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7291 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7292 ]
7293 );
7294 });
7295
7296 view.update(cx, |view, cx| {
7297 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7298 assert_eq!(view.display_text(cx), "ab\n de");
7299 assert_eq!(
7300 view.selections.display_ranges(cx),
7301 &[
7302 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7303 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7304 ]
7305 );
7306 });
7307
7308 view.update(cx, |view, cx| {
7309 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7310 assert_eq!(view.display_text(cx), "\n");
7311 assert_eq!(
7312 view.selections.display_ranges(cx),
7313 &[
7314 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7315 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7316 ]
7317 );
7318 });
7319 }
7320
7321 #[gpui::test]
7322 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7323 cx.set_global(Settings::test(cx));
7324 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7325 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7326 view.update(cx, |view, cx| {
7327 view.change_selections(None, cx, |s| {
7328 s.select_display_ranges([
7329 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7330 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7331 ])
7332 });
7333
7334 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7335 assert_selection_ranges(
7336 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7337 vec![('<', '>'), ('[', ']')],
7338 view,
7339 cx,
7340 );
7341
7342 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7343 assert_selection_ranges(
7344 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7345 vec![('<', '>'), ('[', ']')],
7346 view,
7347 cx,
7348 );
7349
7350 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7351 assert_selection_ranges(
7352 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7353 vec![('<', '>'), ('[', ']')],
7354 view,
7355 cx,
7356 );
7357
7358 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7359 assert_selection_ranges(
7360 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7361 vec![('<', '>'), ('[', ']')],
7362 view,
7363 cx,
7364 );
7365
7366 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7367 assert_selection_ranges(
7368 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7369 vec![('<', '>'), ('[', ']')],
7370 view,
7371 cx,
7372 );
7373
7374 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7375 assert_selection_ranges(
7376 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7377 vec![('<', '>'), ('[', ']')],
7378 view,
7379 cx,
7380 );
7381
7382 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7383 assert_selection_ranges(
7384 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7385 vec![('<', '>'), ('[', ']')],
7386 view,
7387 cx,
7388 );
7389
7390 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7391 assert_selection_ranges(
7392 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7393 vec![('<', '>'), ('[', ']')],
7394 view,
7395 cx,
7396 );
7397
7398 view.move_right(&MoveRight, cx);
7399 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7400 assert_selection_ranges(
7401 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7402 vec![('<', '>'), ('[', ']')],
7403 view,
7404 cx,
7405 );
7406
7407 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7408 assert_selection_ranges(
7409 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7410 vec![('<', '>'), ('[', ']')],
7411 view,
7412 cx,
7413 );
7414
7415 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7416 assert_selection_ranges(
7417 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7418 vec![('<', '>'), ('[', ']')],
7419 view,
7420 cx,
7421 );
7422 });
7423 }
7424
7425 #[gpui::test]
7426 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7427 cx.set_global(Settings::test(cx));
7428 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7429 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7430
7431 view.update(cx, |view, cx| {
7432 view.set_wrap_width(Some(140.), cx);
7433 assert_eq!(
7434 view.display_text(cx),
7435 "use one::{\n two::three::\n four::five\n};"
7436 );
7437
7438 view.change_selections(None, cx, |s| {
7439 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7440 });
7441
7442 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7443 assert_eq!(
7444 view.selections.display_ranges(cx),
7445 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7446 );
7447
7448 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7449 assert_eq!(
7450 view.selections.display_ranges(cx),
7451 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7452 );
7453
7454 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7455 assert_eq!(
7456 view.selections.display_ranges(cx),
7457 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7458 );
7459
7460 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7461 assert_eq!(
7462 view.selections.display_ranges(cx),
7463 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7464 );
7465
7466 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7467 assert_eq!(
7468 view.selections.display_ranges(cx),
7469 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7470 );
7471
7472 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7473 assert_eq!(
7474 view.selections.display_ranges(cx),
7475 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7476 );
7477 });
7478 }
7479
7480 #[gpui::test]
7481 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7482 cx.set_global(Settings::test(cx));
7483 let (text, ranges) = marked_text_ranges("one [two three] four");
7484 let buffer = MultiBuffer::build_simple(&text, cx);
7485
7486 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7487
7488 editor.update(cx, |editor, cx| {
7489 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7490 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7491 assert_eq!(editor.text(cx), " four");
7492 });
7493 }
7494
7495 #[gpui::test]
7496 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7497 cx.set_global(Settings::test(cx));
7498 let buffer = MultiBuffer::build_simple("one two three four", cx);
7499 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7500
7501 view.update(cx, |view, cx| {
7502 view.change_selections(None, cx, |s| {
7503 s.select_display_ranges([
7504 // an empty selection - the preceding word fragment is deleted
7505 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7506 // characters selected - they are deleted
7507 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7508 ])
7509 });
7510 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7511 });
7512
7513 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7514
7515 view.update(cx, |view, cx| {
7516 view.change_selections(None, cx, |s| {
7517 s.select_display_ranges([
7518 // an empty selection - the following word fragment is deleted
7519 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7520 // characters selected - they are deleted
7521 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7522 ])
7523 });
7524 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7525 });
7526
7527 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7528 }
7529
7530 #[gpui::test]
7531 fn test_newline(cx: &mut gpui::MutableAppContext) {
7532 cx.set_global(Settings::test(cx));
7533 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7534 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7535
7536 view.update(cx, |view, cx| {
7537 view.change_selections(None, cx, |s| {
7538 s.select_display_ranges([
7539 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7540 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7541 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7542 ])
7543 });
7544
7545 view.newline(&Newline, cx);
7546 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7547 });
7548 }
7549
7550 #[gpui::test]
7551 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7552 cx.set_global(Settings::test(cx));
7553 let buffer = MultiBuffer::build_simple(
7554 "
7555 a
7556 b(
7557 X
7558 )
7559 c(
7560 X
7561 )
7562 "
7563 .unindent()
7564 .as_str(),
7565 cx,
7566 );
7567
7568 let (_, editor) = cx.add_window(Default::default(), |cx| {
7569 let mut editor = build_editor(buffer.clone(), cx);
7570 editor.change_selections(None, cx, |s| {
7571 s.select_ranges([
7572 Point::new(2, 4)..Point::new(2, 5),
7573 Point::new(5, 4)..Point::new(5, 5),
7574 ])
7575 });
7576 editor
7577 });
7578
7579 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7580 buffer.update(cx, |buffer, cx| {
7581 buffer.edit(
7582 [
7583 (Point::new(1, 2)..Point::new(3, 0), ""),
7584 (Point::new(4, 2)..Point::new(6, 0), ""),
7585 ],
7586 cx,
7587 );
7588 assert_eq!(
7589 buffer.read(cx).text(),
7590 "
7591 a
7592 b()
7593 c()
7594 "
7595 .unindent()
7596 );
7597 });
7598
7599 editor.update(cx, |editor, cx| {
7600 assert_eq!(
7601 editor.selections.ranges(cx),
7602 &[
7603 Point::new(1, 2)..Point::new(1, 2),
7604 Point::new(2, 2)..Point::new(2, 2),
7605 ],
7606 );
7607
7608 editor.newline(&Newline, cx);
7609 assert_eq!(
7610 editor.text(cx),
7611 "
7612 a
7613 b(
7614 )
7615 c(
7616 )
7617 "
7618 .unindent()
7619 );
7620
7621 // The selections are moved after the inserted newlines
7622 assert_eq!(
7623 editor.selections.ranges(cx),
7624 &[
7625 Point::new(2, 0)..Point::new(2, 0),
7626 Point::new(4, 0)..Point::new(4, 0),
7627 ],
7628 );
7629 });
7630 }
7631
7632 #[gpui::test]
7633 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7634 cx.set_global(Settings::test(cx));
7635 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7636 let (_, editor) = cx.add_window(Default::default(), |cx| {
7637 let mut editor = build_editor(buffer.clone(), cx);
7638 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7639 editor
7640 });
7641
7642 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7643 buffer.update(cx, |buffer, cx| {
7644 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7645 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7646 });
7647
7648 editor.update(cx, |editor, cx| {
7649 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7650
7651 editor.insert("Z", cx);
7652 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7653
7654 // The selections are moved after the inserted characters
7655 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7656 });
7657 }
7658
7659 #[gpui::test]
7660 async fn test_tab(cx: &mut gpui::TestAppContext) {
7661 let mut cx = EditorTestContext::new(cx).await;
7662 cx.update(|cx| {
7663 cx.update_global::<Settings, _, _>(|settings, _| {
7664 settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
7665 });
7666 });
7667 cx.set_state(indoc! {"
7668 |ab|c
7669 |🏀|🏀|efg
7670 d|
7671 "});
7672 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7673 cx.assert_editor_state(indoc! {"
7674 |ab |c
7675 |🏀 |🏀 |efg
7676 d |
7677 "});
7678 }
7679
7680 #[gpui::test]
7681 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7682 let mut cx = EditorTestContext::new(cx).await;
7683
7684 cx.set_state(indoc! {"
7685 [one} [two}
7686 three
7687 four"});
7688 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7689 cx.assert_editor_state(indoc! {"
7690 [one} [two}
7691 three
7692 four"});
7693
7694 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7695 cx.assert_editor_state(indoc! {"
7696 [one} [two}
7697 three
7698 four"});
7699
7700 // select across line ending
7701 cx.set_state(indoc! {"
7702 one two
7703 t[hree
7704 } four"});
7705 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7706 cx.assert_editor_state(indoc! {"
7707 one two
7708 t[hree
7709 } four"});
7710
7711 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7712 cx.assert_editor_state(indoc! {"
7713 one two
7714 t[hree
7715 } four"});
7716
7717 // Ensure that indenting/outdenting works when the cursor is at column 0.
7718 cx.set_state(indoc! {"
7719 one two
7720 |three
7721 four"});
7722 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7723 cx.assert_editor_state(indoc! {"
7724 one two
7725 |three
7726 four"});
7727
7728 cx.set_state(indoc! {"
7729 one two
7730 | three
7731 four"});
7732 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7733 cx.assert_editor_state(indoc! {"
7734 one two
7735 |three
7736 four"});
7737 }
7738
7739 #[gpui::test]
7740 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7741 let mut cx = EditorTestContext::new(cx).await;
7742 cx.update(|cx| {
7743 cx.update_global::<Settings, _, _>(|settings, _| {
7744 settings.editor_overrides.hard_tabs = Some(true);
7745 });
7746 });
7747
7748 // select two ranges on one line
7749 cx.set_state(indoc! {"
7750 [one} [two}
7751 three
7752 four"});
7753 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7754 cx.assert_editor_state(indoc! {"
7755 \t[one} [two}
7756 three
7757 four"});
7758 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7759 cx.assert_editor_state(indoc! {"
7760 \t\t[one} [two}
7761 three
7762 four"});
7763 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7764 cx.assert_editor_state(indoc! {"
7765 \t[one} [two}
7766 three
7767 four"});
7768 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7769 cx.assert_editor_state(indoc! {"
7770 [one} [two}
7771 three
7772 four"});
7773
7774 // select across a line ending
7775 cx.set_state(indoc! {"
7776 one two
7777 t[hree
7778 }four"});
7779 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7780 cx.assert_editor_state(indoc! {"
7781 one two
7782 \tt[hree
7783 }four"});
7784 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7785 cx.assert_editor_state(indoc! {"
7786 one two
7787 \t\tt[hree
7788 }four"});
7789 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7790 cx.assert_editor_state(indoc! {"
7791 one two
7792 \tt[hree
7793 }four"});
7794 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7795 cx.assert_editor_state(indoc! {"
7796 one two
7797 t[hree
7798 }four"});
7799
7800 // Ensure that indenting/outdenting works when the cursor is at column 0.
7801 cx.set_state(indoc! {"
7802 one two
7803 |three
7804 four"});
7805 cx.assert_editor_state(indoc! {"
7806 one two
7807 |three
7808 four"});
7809 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7810 cx.assert_editor_state(indoc! {"
7811 one two
7812 \t|three
7813 four"});
7814 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7815 cx.assert_editor_state(indoc! {"
7816 one two
7817 |three
7818 four"});
7819 }
7820
7821 #[gpui::test]
7822 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7823 cx.set_global(
7824 Settings::test(cx)
7825 .with_language_defaults(
7826 "TOML",
7827 EditorSettings {
7828 tab_size: Some(2.try_into().unwrap()),
7829 ..Default::default()
7830 },
7831 )
7832 .with_language_defaults(
7833 "Rust",
7834 EditorSettings {
7835 tab_size: Some(4.try_into().unwrap()),
7836 ..Default::default()
7837 },
7838 ),
7839 );
7840 let toml_language = Arc::new(Language::new(
7841 LanguageConfig {
7842 name: "TOML".into(),
7843 ..Default::default()
7844 },
7845 None,
7846 ));
7847 let rust_language = Arc::new(Language::new(
7848 LanguageConfig {
7849 name: "Rust".into(),
7850 ..Default::default()
7851 },
7852 None,
7853 ));
7854
7855 let toml_buffer = cx
7856 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7857 let rust_buffer = cx.add_model(|cx| {
7858 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7859 });
7860 let multibuffer = cx.add_model(|cx| {
7861 let mut multibuffer = MultiBuffer::new(0);
7862 multibuffer.push_excerpts(
7863 toml_buffer.clone(),
7864 [ExcerptRange {
7865 context: Point::new(0, 0)..Point::new(2, 0),
7866 primary: None,
7867 }],
7868 cx,
7869 );
7870 multibuffer.push_excerpts(
7871 rust_buffer.clone(),
7872 [ExcerptRange {
7873 context: Point::new(0, 0)..Point::new(1, 0),
7874 primary: None,
7875 }],
7876 cx,
7877 );
7878 multibuffer
7879 });
7880
7881 cx.add_window(Default::default(), |cx| {
7882 let mut editor = build_editor(multibuffer, cx);
7883
7884 assert_eq!(
7885 editor.text(cx),
7886 indoc! {"
7887 a = 1
7888 b = 2
7889
7890 const c: usize = 3;
7891 "}
7892 );
7893
7894 select_ranges(
7895 &mut editor,
7896 indoc! {"
7897 [a] = 1
7898 b = 2
7899
7900 [const c:] usize = 3;
7901 "},
7902 cx,
7903 );
7904
7905 editor.tab(&Tab, cx);
7906 assert_text_with_selections(
7907 &mut editor,
7908 indoc! {"
7909 [a] = 1
7910 b = 2
7911
7912 [const c:] usize = 3;
7913 "},
7914 cx,
7915 );
7916 editor.tab_prev(&TabPrev, cx);
7917 assert_text_with_selections(
7918 &mut editor,
7919 indoc! {"
7920 [a] = 1
7921 b = 2
7922
7923 [const c:] usize = 3;
7924 "},
7925 cx,
7926 );
7927
7928 editor
7929 });
7930 }
7931
7932 #[gpui::test]
7933 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7934 let mut cx = EditorTestContext::new(cx).await;
7935 // Basic backspace
7936 cx.set_state(indoc! {"
7937 on|e two three
7938 fou[r} five six
7939 seven {eight nine
7940 ]ten"});
7941 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7942 cx.assert_editor_state(indoc! {"
7943 o|e two three
7944 fou| five six
7945 seven |ten"});
7946
7947 // Test backspace inside and around indents
7948 cx.set_state(indoc! {"
7949 zero
7950 |one
7951 |two
7952 | | | three
7953 | | four"});
7954 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7955 cx.assert_editor_state(indoc! {"
7956 zero
7957 |one
7958 |two
7959 | three| four"});
7960
7961 // Test backspace with line_mode set to true
7962 cx.update_editor(|e, _| e.selections.line_mode = true);
7963 cx.set_state(indoc! {"
7964 The |quick |brown
7965 fox jumps over
7966 the lazy dog
7967 |The qu[ick b}rown"});
7968 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7969 cx.assert_editor_state(indoc! {"
7970 |fox jumps over
7971 the lazy dog|"});
7972 }
7973
7974 #[gpui::test]
7975 async fn test_delete(cx: &mut gpui::TestAppContext) {
7976 let mut cx = EditorTestContext::new(cx).await;
7977
7978 cx.set_state(indoc! {"
7979 on|e two three
7980 fou[r} five six
7981 seven {eight nine
7982 ]ten"});
7983 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7984 cx.assert_editor_state(indoc! {"
7985 on| two three
7986 fou| five six
7987 seven |ten"});
7988
7989 // Test backspace with line_mode set to true
7990 cx.update_editor(|e, _| e.selections.line_mode = true);
7991 cx.set_state(indoc! {"
7992 The |quick |brown
7993 fox {jum]ps over
7994 the lazy dog
7995 |The qu[ick b}rown"});
7996 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7997 cx.assert_editor_state("|the lazy dog|");
7998 }
7999
8000 #[gpui::test]
8001 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8002 cx.set_global(Settings::test(cx));
8003 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8004 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8005 view.update(cx, |view, cx| {
8006 view.change_selections(None, cx, |s| {
8007 s.select_display_ranges([
8008 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8009 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8010 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8011 ])
8012 });
8013 view.delete_line(&DeleteLine, cx);
8014 assert_eq!(view.display_text(cx), "ghi");
8015 assert_eq!(
8016 view.selections.display_ranges(cx),
8017 vec![
8018 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8019 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8020 ]
8021 );
8022 });
8023
8024 cx.set_global(Settings::test(cx));
8025 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8026 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8027 view.update(cx, |view, cx| {
8028 view.change_selections(None, cx, |s| {
8029 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
8030 });
8031 view.delete_line(&DeleteLine, cx);
8032 assert_eq!(view.display_text(cx), "ghi\n");
8033 assert_eq!(
8034 view.selections.display_ranges(cx),
8035 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8036 );
8037 });
8038 }
8039
8040 #[gpui::test]
8041 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8042 cx.set_global(Settings::test(cx));
8043 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8044 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8045 view.update(cx, |view, cx| {
8046 view.change_selections(None, cx, |s| {
8047 s.select_display_ranges([
8048 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8049 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8050 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8051 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8052 ])
8053 });
8054 view.duplicate_line(&DuplicateLine, cx);
8055 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8056 assert_eq!(
8057 view.selections.display_ranges(cx),
8058 vec![
8059 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8060 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8061 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8062 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8063 ]
8064 );
8065 });
8066
8067 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8068 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8069 view.update(cx, |view, cx| {
8070 view.change_selections(None, cx, |s| {
8071 s.select_display_ranges([
8072 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8073 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8074 ])
8075 });
8076 view.duplicate_line(&DuplicateLine, cx);
8077 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8078 assert_eq!(
8079 view.selections.display_ranges(cx),
8080 vec![
8081 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8082 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8083 ]
8084 );
8085 });
8086 }
8087
8088 #[gpui::test]
8089 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8090 cx.set_global(Settings::test(cx));
8091 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8092 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8093 view.update(cx, |view, cx| {
8094 view.fold_ranges(
8095 vec![
8096 Point::new(0, 2)..Point::new(1, 2),
8097 Point::new(2, 3)..Point::new(4, 1),
8098 Point::new(7, 0)..Point::new(8, 4),
8099 ],
8100 cx,
8101 );
8102 view.change_selections(None, cx, |s| {
8103 s.select_display_ranges([
8104 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8105 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8106 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8107 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8108 ])
8109 });
8110 assert_eq!(
8111 view.display_text(cx),
8112 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8113 );
8114
8115 view.move_line_up(&MoveLineUp, cx);
8116 assert_eq!(
8117 view.display_text(cx),
8118 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8119 );
8120 assert_eq!(
8121 view.selections.display_ranges(cx),
8122 vec![
8123 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8124 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8125 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8126 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8127 ]
8128 );
8129 });
8130
8131 view.update(cx, |view, cx| {
8132 view.move_line_down(&MoveLineDown, cx);
8133 assert_eq!(
8134 view.display_text(cx),
8135 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8136 );
8137 assert_eq!(
8138 view.selections.display_ranges(cx),
8139 vec![
8140 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8141 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8142 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8143 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8144 ]
8145 );
8146 });
8147
8148 view.update(cx, |view, cx| {
8149 view.move_line_down(&MoveLineDown, cx);
8150 assert_eq!(
8151 view.display_text(cx),
8152 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8153 );
8154 assert_eq!(
8155 view.selections.display_ranges(cx),
8156 vec![
8157 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8158 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8159 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8160 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8161 ]
8162 );
8163 });
8164
8165 view.update(cx, |view, cx| {
8166 view.move_line_up(&MoveLineUp, cx);
8167 assert_eq!(
8168 view.display_text(cx),
8169 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8170 );
8171 assert_eq!(
8172 view.selections.display_ranges(cx),
8173 vec![
8174 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8175 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8176 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8177 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8178 ]
8179 );
8180 });
8181 }
8182
8183 #[gpui::test]
8184 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8185 cx.set_global(Settings::test(cx));
8186 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8187 let snapshot = buffer.read(cx).snapshot(cx);
8188 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8189 editor.update(cx, |editor, cx| {
8190 editor.insert_blocks(
8191 [BlockProperties {
8192 style: BlockStyle::Fixed,
8193 position: snapshot.anchor_after(Point::new(2, 0)),
8194 disposition: BlockDisposition::Below,
8195 height: 1,
8196 render: Arc::new(|_| Empty::new().boxed()),
8197 }],
8198 cx,
8199 );
8200 editor.change_selections(None, cx, |s| {
8201 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8202 });
8203 editor.move_line_down(&MoveLineDown, cx);
8204 });
8205 }
8206
8207 #[gpui::test]
8208 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8209 cx.set_global(Settings::test(cx));
8210
8211 cx.add_window(Default::default(), |cx| {
8212 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8213
8214 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8215 editor.transpose(&Default::default(), cx);
8216 assert_eq!(editor.text(cx), "bac");
8217 assert_eq!(editor.selections.ranges(cx), [2..2]);
8218
8219 editor.transpose(&Default::default(), cx);
8220 assert_eq!(editor.text(cx), "bca");
8221 assert_eq!(editor.selections.ranges(cx), [3..3]);
8222
8223 editor.transpose(&Default::default(), cx);
8224 assert_eq!(editor.text(cx), "bac");
8225 assert_eq!(editor.selections.ranges(cx), [3..3]);
8226
8227 editor
8228 })
8229 .1;
8230
8231 cx.add_window(Default::default(), |cx| {
8232 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8233
8234 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8235 editor.transpose(&Default::default(), cx);
8236 assert_eq!(editor.text(cx), "acb\nde");
8237 assert_eq!(editor.selections.ranges(cx), [3..3]);
8238
8239 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8240 editor.transpose(&Default::default(), cx);
8241 assert_eq!(editor.text(cx), "acbd\ne");
8242 assert_eq!(editor.selections.ranges(cx), [5..5]);
8243
8244 editor.transpose(&Default::default(), cx);
8245 assert_eq!(editor.text(cx), "acbde\n");
8246 assert_eq!(editor.selections.ranges(cx), [6..6]);
8247
8248 editor.transpose(&Default::default(), cx);
8249 assert_eq!(editor.text(cx), "acbd\ne");
8250 assert_eq!(editor.selections.ranges(cx), [6..6]);
8251
8252 editor
8253 })
8254 .1;
8255
8256 cx.add_window(Default::default(), |cx| {
8257 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8258
8259 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8260 editor.transpose(&Default::default(), cx);
8261 assert_eq!(editor.text(cx), "bacd\ne");
8262 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8263
8264 editor.transpose(&Default::default(), cx);
8265 assert_eq!(editor.text(cx), "bcade\n");
8266 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8267
8268 editor.transpose(&Default::default(), cx);
8269 assert_eq!(editor.text(cx), "bcda\ne");
8270 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8271
8272 editor.transpose(&Default::default(), cx);
8273 assert_eq!(editor.text(cx), "bcade\n");
8274 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8275
8276 editor.transpose(&Default::default(), cx);
8277 assert_eq!(editor.text(cx), "bcaed\n");
8278 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8279
8280 editor
8281 })
8282 .1;
8283
8284 cx.add_window(Default::default(), |cx| {
8285 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8286
8287 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8288 editor.transpose(&Default::default(), cx);
8289 assert_eq!(editor.text(cx), "🏀🍐✋");
8290 assert_eq!(editor.selections.ranges(cx), [8..8]);
8291
8292 editor.transpose(&Default::default(), cx);
8293 assert_eq!(editor.text(cx), "🏀✋🍐");
8294 assert_eq!(editor.selections.ranges(cx), [11..11]);
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
8301 })
8302 .1;
8303 }
8304
8305 #[gpui::test]
8306 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8307 let mut cx = EditorTestContext::new(cx).await;
8308
8309 cx.set_state("[one✅ }two [three }four [five }six ");
8310 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8311 cx.assert_editor_state("|two |four |six ");
8312
8313 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8314 cx.set_state("two |four |six |");
8315 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8316 cx.assert_editor_state("two one✅ |four three |six five |");
8317
8318 // Paste again but with only two cursors. Since the number of cursors doesn't
8319 // match the number of slices in the clipboard, the entire clipboard text
8320 // is pasted at each cursor.
8321 cx.set_state("|two one✅ four three six five |");
8322 cx.update_editor(|e, cx| {
8323 e.handle_input("( ", cx);
8324 e.paste(&Paste, cx);
8325 e.handle_input(") ", cx);
8326 });
8327 cx.assert_editor_state(indoc! {"
8328 ( one✅
8329 three
8330 five ) |two one✅ four three six five ( one✅
8331 three
8332 five ) |"});
8333
8334 // Cut with three selections, one of which is full-line.
8335 cx.set_state(indoc! {"
8336 1[2}3
8337 4|567
8338 [8}9"});
8339 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8340 cx.assert_editor_state(indoc! {"
8341 1|3
8342 |9"});
8343
8344 // Paste with three selections, noticing how the copied selection that was full-line
8345 // gets inserted before the second cursor.
8346 cx.set_state(indoc! {"
8347 1|3
8348 9|
8349 [o}ne"});
8350 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8351 cx.assert_editor_state(indoc! {"
8352 12|3
8353 4567
8354 9|
8355 8|ne"});
8356
8357 // Copy with a single cursor only, which writes the whole line into the clipboard.
8358 cx.set_state(indoc! {"
8359 The quick brown
8360 fox ju|mps over
8361 the lazy dog"});
8362 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8363 cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
8364
8365 // Paste with three selections, noticing how the copied full-line selection is inserted
8366 // before the empty selections but replaces the selection that is non-empty.
8367 cx.set_state(indoc! {"
8368 T|he quick brown
8369 [fo}x jumps over
8370 t|he lazy dog"});
8371 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8372 cx.assert_editor_state(indoc! {"
8373 fox jumps over
8374 T|he quick brown
8375 fox jumps over
8376 |x jumps over
8377 fox jumps over
8378 t|he lazy dog"});
8379 }
8380
8381 #[gpui::test]
8382 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8383 cx.set_global(Settings::test(cx));
8384 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8385 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8386 view.update(cx, |view, cx| {
8387 view.select_all(&SelectAll, cx);
8388 assert_eq!(
8389 view.selections.display_ranges(cx),
8390 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8391 );
8392 });
8393 }
8394
8395 #[gpui::test]
8396 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8397 cx.set_global(Settings::test(cx));
8398 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8399 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8400 view.update(cx, |view, cx| {
8401 view.change_selections(None, cx, |s| {
8402 s.select_display_ranges([
8403 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8404 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8405 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8406 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8407 ])
8408 });
8409 view.select_line(&SelectLine, cx);
8410 assert_eq!(
8411 view.selections.display_ranges(cx),
8412 vec![
8413 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8414 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8415 ]
8416 );
8417 });
8418
8419 view.update(cx, |view, cx| {
8420 view.select_line(&SelectLine, cx);
8421 assert_eq!(
8422 view.selections.display_ranges(cx),
8423 vec![
8424 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8425 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8426 ]
8427 );
8428 });
8429
8430 view.update(cx, |view, cx| {
8431 view.select_line(&SelectLine, cx);
8432 assert_eq!(
8433 view.selections.display_ranges(cx),
8434 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8435 );
8436 });
8437 }
8438
8439 #[gpui::test]
8440 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8441 cx.set_global(Settings::test(cx));
8442 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8443 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8444 view.update(cx, |view, cx| {
8445 view.fold_ranges(
8446 vec![
8447 Point::new(0, 2)..Point::new(1, 2),
8448 Point::new(2, 3)..Point::new(4, 1),
8449 Point::new(7, 0)..Point::new(8, 4),
8450 ],
8451 cx,
8452 );
8453 view.change_selections(None, cx, |s| {
8454 s.select_display_ranges([
8455 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8456 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8457 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8458 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8459 ])
8460 });
8461 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8462 });
8463
8464 view.update(cx, |view, cx| {
8465 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8466 assert_eq!(
8467 view.display_text(cx),
8468 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8469 );
8470 assert_eq!(
8471 view.selections.display_ranges(cx),
8472 [
8473 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8474 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8475 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8476 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8477 ]
8478 );
8479 });
8480
8481 view.update(cx, |view, cx| {
8482 view.change_selections(None, cx, |s| {
8483 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8484 });
8485 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8486 assert_eq!(
8487 view.display_text(cx),
8488 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8489 );
8490 assert_eq!(
8491 view.selections.display_ranges(cx),
8492 [
8493 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8494 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8495 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8496 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8497 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8498 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8499 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8500 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8501 ]
8502 );
8503 });
8504 }
8505
8506 #[gpui::test]
8507 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8508 cx.set_global(Settings::test(cx));
8509 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8510 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8511
8512 view.update(cx, |view, cx| {
8513 view.change_selections(None, cx, |s| {
8514 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8515 });
8516 });
8517 view.update(cx, |view, cx| {
8518 view.add_selection_above(&AddSelectionAbove, cx);
8519 assert_eq!(
8520 view.selections.display_ranges(cx),
8521 vec![
8522 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8523 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8524 ]
8525 );
8526 });
8527
8528 view.update(cx, |view, cx| {
8529 view.add_selection_above(&AddSelectionAbove, cx);
8530 assert_eq!(
8531 view.selections.display_ranges(cx),
8532 vec![
8533 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8534 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8535 ]
8536 );
8537 });
8538
8539 view.update(cx, |view, cx| {
8540 view.add_selection_below(&AddSelectionBelow, cx);
8541 assert_eq!(
8542 view.selections.display_ranges(cx),
8543 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8544 );
8545
8546 view.undo_selection(&UndoSelection, cx);
8547 assert_eq!(
8548 view.selections.display_ranges(cx),
8549 vec![
8550 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8551 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8552 ]
8553 );
8554
8555 view.redo_selection(&RedoSelection, cx);
8556 assert_eq!(
8557 view.selections.display_ranges(cx),
8558 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8559 );
8560 });
8561
8562 view.update(cx, |view, cx| {
8563 view.add_selection_below(&AddSelectionBelow, cx);
8564 assert_eq!(
8565 view.selections.display_ranges(cx),
8566 vec![
8567 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8568 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8569 ]
8570 );
8571 });
8572
8573 view.update(cx, |view, cx| {
8574 view.add_selection_below(&AddSelectionBelow, cx);
8575 assert_eq!(
8576 view.selections.display_ranges(cx),
8577 vec![
8578 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8579 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8580 ]
8581 );
8582 });
8583
8584 view.update(cx, |view, cx| {
8585 view.change_selections(None, cx, |s| {
8586 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8587 });
8588 });
8589 view.update(cx, |view, cx| {
8590 view.add_selection_below(&AddSelectionBelow, cx);
8591 assert_eq!(
8592 view.selections.display_ranges(cx),
8593 vec![
8594 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8595 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8596 ]
8597 );
8598 });
8599
8600 view.update(cx, |view, cx| {
8601 view.add_selection_below(&AddSelectionBelow, cx);
8602 assert_eq!(
8603 view.selections.display_ranges(cx),
8604 vec![
8605 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8606 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8607 ]
8608 );
8609 });
8610
8611 view.update(cx, |view, cx| {
8612 view.add_selection_above(&AddSelectionAbove, cx);
8613 assert_eq!(
8614 view.selections.display_ranges(cx),
8615 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8616 );
8617 });
8618
8619 view.update(cx, |view, cx| {
8620 view.add_selection_above(&AddSelectionAbove, cx);
8621 assert_eq!(
8622 view.selections.display_ranges(cx),
8623 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8624 );
8625 });
8626
8627 view.update(cx, |view, cx| {
8628 view.change_selections(None, cx, |s| {
8629 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8630 });
8631 view.add_selection_below(&AddSelectionBelow, cx);
8632 assert_eq!(
8633 view.selections.display_ranges(cx),
8634 vec![
8635 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8636 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8637 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8638 ]
8639 );
8640 });
8641
8642 view.update(cx, |view, cx| {
8643 view.add_selection_below(&AddSelectionBelow, cx);
8644 assert_eq!(
8645 view.selections.display_ranges(cx),
8646 vec![
8647 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8648 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8649 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8650 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8651 ]
8652 );
8653 });
8654
8655 view.update(cx, |view, cx| {
8656 view.add_selection_above(&AddSelectionAbove, cx);
8657 assert_eq!(
8658 view.selections.display_ranges(cx),
8659 vec![
8660 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8661 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8662 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8663 ]
8664 );
8665 });
8666
8667 view.update(cx, |view, cx| {
8668 view.change_selections(None, cx, |s| {
8669 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8670 });
8671 });
8672 view.update(cx, |view, cx| {
8673 view.add_selection_above(&AddSelectionAbove, cx);
8674 assert_eq!(
8675 view.selections.display_ranges(cx),
8676 vec![
8677 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8678 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8679 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8680 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8681 ]
8682 );
8683 });
8684
8685 view.update(cx, |view, cx| {
8686 view.add_selection_below(&AddSelectionBelow, cx);
8687 assert_eq!(
8688 view.selections.display_ranges(cx),
8689 vec![
8690 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8691 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8692 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8693 ]
8694 );
8695 });
8696 }
8697
8698 #[gpui::test]
8699 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8700 cx.set_global(Settings::test(cx));
8701
8702 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8703 let buffer = MultiBuffer::build_simple(&text, cx);
8704 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8705
8706 view.update(cx, |view, cx| {
8707 view.change_selections(None, cx, |s| {
8708 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8709 });
8710 view.select_next(
8711 &SelectNext {
8712 replace_newest: false,
8713 },
8714 cx,
8715 );
8716 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8717
8718 view.select_next(
8719 &SelectNext {
8720 replace_newest: false,
8721 },
8722 cx,
8723 );
8724 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8725
8726 view.undo_selection(&UndoSelection, cx);
8727 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8728
8729 view.redo_selection(&RedoSelection, cx);
8730 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8731
8732 view.select_next(
8733 &SelectNext {
8734 replace_newest: false,
8735 },
8736 cx,
8737 );
8738 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8739
8740 view.select_next(
8741 &SelectNext {
8742 replace_newest: false,
8743 },
8744 cx,
8745 );
8746 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8747 });
8748 }
8749
8750 #[gpui::test]
8751 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8752 cx.update(|cx| cx.set_global(Settings::test(cx)));
8753 let language = Arc::new(Language::new(
8754 LanguageConfig::default(),
8755 Some(tree_sitter_rust::language()),
8756 ));
8757
8758 let text = r#"
8759 use mod1::mod2::{mod3, mod4};
8760
8761 fn fn_1(param1: bool, param2: &str) {
8762 let var1 = "text";
8763 }
8764 "#
8765 .unindent();
8766
8767 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8768 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8769 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8770 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8771 .await;
8772
8773 view.update(cx, |view, cx| {
8774 view.change_selections(None, cx, |s| {
8775 s.select_display_ranges([
8776 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8777 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8778 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8779 ]);
8780 });
8781 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8782 });
8783 assert_eq!(
8784 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8785 &[
8786 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8787 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8788 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8789 ]
8790 );
8791
8792 view.update(cx, |view, cx| {
8793 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8794 });
8795 assert_eq!(
8796 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8797 &[
8798 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8799 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8800 ]
8801 );
8802
8803 view.update(cx, |view, cx| {
8804 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8805 });
8806 assert_eq!(
8807 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8808 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8809 );
8810
8811 // Trying to expand the selected syntax node one more time has no effect.
8812 view.update(cx, |view, cx| {
8813 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8814 });
8815 assert_eq!(
8816 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8817 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8818 );
8819
8820 view.update(cx, |view, cx| {
8821 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8822 });
8823 assert_eq!(
8824 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8825 &[
8826 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8827 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8828 ]
8829 );
8830
8831 view.update(cx, |view, cx| {
8832 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8833 });
8834 assert_eq!(
8835 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8836 &[
8837 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8838 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8839 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8840 ]
8841 );
8842
8843 view.update(cx, |view, cx| {
8844 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8845 });
8846 assert_eq!(
8847 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8848 &[
8849 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8850 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8851 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8852 ]
8853 );
8854
8855 // Trying to shrink the selected syntax node one more time has no effect.
8856 view.update(cx, |view, cx| {
8857 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8858 });
8859 assert_eq!(
8860 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8861 &[
8862 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8863 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8864 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8865 ]
8866 );
8867
8868 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8869 // a fold.
8870 view.update(cx, |view, cx| {
8871 view.fold_ranges(
8872 vec![
8873 Point::new(0, 21)..Point::new(0, 24),
8874 Point::new(3, 20)..Point::new(3, 22),
8875 ],
8876 cx,
8877 );
8878 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8879 });
8880 assert_eq!(
8881 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8882 &[
8883 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8884 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8885 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8886 ]
8887 );
8888 }
8889
8890 #[gpui::test]
8891 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8892 cx.update(|cx| cx.set_global(Settings::test(cx)));
8893 let language = Arc::new(
8894 Language::new(
8895 LanguageConfig {
8896 brackets: vec![
8897 BracketPair {
8898 start: "{".to_string(),
8899 end: "}".to_string(),
8900 close: false,
8901 newline: true,
8902 },
8903 BracketPair {
8904 start: "(".to_string(),
8905 end: ")".to_string(),
8906 close: false,
8907 newline: true,
8908 },
8909 ],
8910 ..Default::default()
8911 },
8912 Some(tree_sitter_rust::language()),
8913 )
8914 .with_indents_query(
8915 r#"
8916 (_ "(" ")" @end) @indent
8917 (_ "{" "}" @end) @indent
8918 "#,
8919 )
8920 .unwrap(),
8921 );
8922
8923 let text = "fn a() {}";
8924
8925 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8926 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8927 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8928 editor
8929 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8930 .await;
8931
8932 editor.update(cx, |editor, cx| {
8933 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8934 editor.newline(&Newline, cx);
8935 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8936 assert_eq!(
8937 editor.selections.ranges(cx),
8938 &[
8939 Point::new(1, 4)..Point::new(1, 4),
8940 Point::new(3, 4)..Point::new(3, 4),
8941 Point::new(5, 0)..Point::new(5, 0)
8942 ]
8943 );
8944 });
8945 }
8946
8947 #[gpui::test]
8948 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8949 cx.update(|cx| cx.set_global(Settings::test(cx)));
8950 let language = Arc::new(Language::new(
8951 LanguageConfig {
8952 brackets: vec![
8953 BracketPair {
8954 start: "{".to_string(),
8955 end: "}".to_string(),
8956 close: true,
8957 newline: true,
8958 },
8959 BracketPair {
8960 start: "/*".to_string(),
8961 end: " */".to_string(),
8962 close: true,
8963 newline: true,
8964 },
8965 BracketPair {
8966 start: "[".to_string(),
8967 end: "]".to_string(),
8968 close: false,
8969 newline: true,
8970 },
8971 ],
8972 autoclose_before: "})]".to_string(),
8973 ..Default::default()
8974 },
8975 Some(tree_sitter_rust::language()),
8976 ));
8977
8978 let text = r#"
8979 a
8980
8981 /
8982
8983 "#
8984 .unindent();
8985
8986 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8987 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8988 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8989 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8990 .await;
8991
8992 view.update(cx, |view, cx| {
8993 view.change_selections(None, cx, |s| {
8994 s.select_display_ranges([
8995 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8996 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8997 ])
8998 });
8999
9000 view.handle_input("{", cx);
9001 view.handle_input("{", cx);
9002 view.handle_input("{", cx);
9003 assert_eq!(
9004 view.text(cx),
9005 "
9006 {{{}}}
9007 {{{}}}
9008 /
9009
9010 "
9011 .unindent()
9012 );
9013
9014 view.move_right(&MoveRight, cx);
9015 view.handle_input("}", cx);
9016 view.handle_input("}", cx);
9017 view.handle_input("}", cx);
9018 assert_eq!(
9019 view.text(cx),
9020 "
9021 {{{}}}}
9022 {{{}}}}
9023 /
9024
9025 "
9026 .unindent()
9027 );
9028
9029 view.undo(&Undo, cx);
9030 view.handle_input("/", cx);
9031 view.handle_input("*", cx);
9032 assert_eq!(
9033 view.text(cx),
9034 "
9035 /* */
9036 /* */
9037 /
9038
9039 "
9040 .unindent()
9041 );
9042
9043 view.undo(&Undo, cx);
9044 view.change_selections(None, cx, |s| {
9045 s.select_display_ranges([
9046 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9047 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9048 ])
9049 });
9050 view.handle_input("*", cx);
9051 assert_eq!(
9052 view.text(cx),
9053 "
9054 a
9055
9056 /*
9057 *
9058 "
9059 .unindent()
9060 );
9061
9062 // Don't autoclose if the next character isn't whitespace and isn't
9063 // listed in the language's "autoclose_before" section.
9064 view.finalize_last_transaction(cx);
9065 view.change_selections(None, cx, |s| {
9066 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
9067 });
9068 view.handle_input("{", cx);
9069 assert_eq!(
9070 view.text(cx),
9071 "
9072 {a
9073
9074 /*
9075 *
9076 "
9077 .unindent()
9078 );
9079
9080 view.undo(&Undo, cx);
9081 view.change_selections(None, cx, |s| {
9082 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
9083 });
9084 view.handle_input("{", cx);
9085 assert_eq!(
9086 view.text(cx),
9087 "
9088 {a}
9089
9090 /*
9091 *
9092 "
9093 .unindent()
9094 );
9095 assert_eq!(
9096 view.selections.display_ranges(cx),
9097 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9098 );
9099
9100 view.undo(&Undo, cx);
9101 view.handle_input("[", cx);
9102 assert_eq!(
9103 view.text(cx),
9104 "
9105 [a]
9106
9107 /*
9108 *
9109 "
9110 .unindent()
9111 );
9112 assert_eq!(
9113 view.selections.display_ranges(cx),
9114 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9115 );
9116
9117 view.undo(&Undo, cx);
9118 view.change_selections(None, cx, |s| {
9119 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
9120 });
9121 view.handle_input("[", cx);
9122 assert_eq!(
9123 view.text(cx),
9124 "
9125 a[
9126
9127 /*
9128 *
9129 "
9130 .unindent()
9131 );
9132 assert_eq!(
9133 view.selections.display_ranges(cx),
9134 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
9135 );
9136 });
9137 }
9138
9139 #[gpui::test]
9140 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9141 cx.update(|cx| cx.set_global(Settings::test(cx)));
9142 let language = Arc::new(Language::new(
9143 LanguageConfig {
9144 brackets: vec![BracketPair {
9145 start: "{".to_string(),
9146 end: "}".to_string(),
9147 close: true,
9148 newline: true,
9149 }],
9150 ..Default::default()
9151 },
9152 Some(tree_sitter_rust::language()),
9153 ));
9154
9155 let text = r#"
9156 a
9157 b
9158 c
9159 "#
9160 .unindent();
9161
9162 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9163 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9164 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9165 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9166 .await;
9167
9168 view.update(cx, |view, cx| {
9169 view.change_selections(None, cx, |s| {
9170 s.select_display_ranges([
9171 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9172 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9173 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9174 ])
9175 });
9176
9177 view.handle_input("{", cx);
9178 view.handle_input("{", cx);
9179 view.handle_input("{", cx);
9180 assert_eq!(
9181 view.text(cx),
9182 "
9183 {{{a}}}
9184 {{{b}}}
9185 {{{c}}}
9186 "
9187 .unindent()
9188 );
9189 assert_eq!(
9190 view.selections.display_ranges(cx),
9191 [
9192 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9193 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9194 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9195 ]
9196 );
9197
9198 view.undo(&Undo, cx);
9199 assert_eq!(
9200 view.text(cx),
9201 "
9202 a
9203 b
9204 c
9205 "
9206 .unindent()
9207 );
9208 assert_eq!(
9209 view.selections.display_ranges(cx),
9210 [
9211 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9212 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9213 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9214 ]
9215 );
9216 });
9217 }
9218
9219 #[gpui::test]
9220 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9221 cx.update(|cx| cx.set_global(Settings::test(cx)));
9222 let language = Arc::new(Language::new(
9223 LanguageConfig {
9224 brackets: vec![BracketPair {
9225 start: "{".to_string(),
9226 end: "}".to_string(),
9227 close: true,
9228 newline: true,
9229 }],
9230 autoclose_before: "}".to_string(),
9231 ..Default::default()
9232 },
9233 Some(tree_sitter_rust::language()),
9234 ));
9235
9236 let text = r#"
9237 a
9238 b
9239 c
9240 "#
9241 .unindent();
9242
9243 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9244 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9245 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9246 editor
9247 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9248 .await;
9249
9250 editor.update(cx, |editor, cx| {
9251 editor.change_selections(None, cx, |s| {
9252 s.select_ranges([
9253 Point::new(0, 1)..Point::new(0, 1),
9254 Point::new(1, 1)..Point::new(1, 1),
9255 Point::new(2, 1)..Point::new(2, 1),
9256 ])
9257 });
9258
9259 editor.handle_input("{", cx);
9260 editor.handle_input("{", cx);
9261 editor.handle_input("_", cx);
9262 assert_eq!(
9263 editor.text(cx),
9264 "
9265 a{{_}}
9266 b{{_}}
9267 c{{_}}
9268 "
9269 .unindent()
9270 );
9271 assert_eq!(
9272 editor.selections.ranges::<Point>(cx),
9273 [
9274 Point::new(0, 4)..Point::new(0, 4),
9275 Point::new(1, 4)..Point::new(1, 4),
9276 Point::new(2, 4)..Point::new(2, 4)
9277 ]
9278 );
9279
9280 editor.backspace(&Default::default(), cx);
9281 editor.backspace(&Default::default(), cx);
9282 assert_eq!(
9283 editor.text(cx),
9284 "
9285 a{}
9286 b{}
9287 c{}
9288 "
9289 .unindent()
9290 );
9291 assert_eq!(
9292 editor.selections.ranges::<Point>(cx),
9293 [
9294 Point::new(0, 2)..Point::new(0, 2),
9295 Point::new(1, 2)..Point::new(1, 2),
9296 Point::new(2, 2)..Point::new(2, 2)
9297 ]
9298 );
9299
9300 editor.delete_to_previous_word_start(&Default::default(), cx);
9301 assert_eq!(
9302 editor.text(cx),
9303 "
9304 a
9305 b
9306 c
9307 "
9308 .unindent()
9309 );
9310 assert_eq!(
9311 editor.selections.ranges::<Point>(cx),
9312 [
9313 Point::new(0, 1)..Point::new(0, 1),
9314 Point::new(1, 1)..Point::new(1, 1),
9315 Point::new(2, 1)..Point::new(2, 1)
9316 ]
9317 );
9318 });
9319 }
9320
9321 #[gpui::test]
9322 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9323 cx.update(|cx| cx.set_global(Settings::test(cx)));
9324
9325 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9326 a.| b
9327 a.| b
9328 a.| b"});
9329 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9330 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9331
9332 editor.update(cx, |editor, cx| {
9333 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9334
9335 editor
9336 .insert_snippet(&insertion_ranges, snippet, cx)
9337 .unwrap();
9338
9339 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9340 let range_markers = ('<', '>');
9341 let (expected_text, mut selection_ranges_lookup) =
9342 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9343 let selection_ranges = selection_ranges_lookup
9344 .remove(&range_markers.into())
9345 .unwrap();
9346 assert_eq!(editor.text(cx), expected_text);
9347 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9348 }
9349 assert(
9350 editor,
9351 cx,
9352 indoc! {"
9353 a.f(<one>, two, <three>) b
9354 a.f(<one>, two, <three>) b
9355 a.f(<one>, two, <three>) b"},
9356 );
9357
9358 // Can't move earlier than the first tab stop
9359 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9360 assert(
9361 editor,
9362 cx,
9363 indoc! {"
9364 a.f(<one>, two, <three>) b
9365 a.f(<one>, two, <three>) b
9366 a.f(<one>, two, <three>) b"},
9367 );
9368
9369 assert!(editor.move_to_next_snippet_tabstop(cx));
9370 assert(
9371 editor,
9372 cx,
9373 indoc! {"
9374 a.f(one, <two>, three) b
9375 a.f(one, <two>, three) b
9376 a.f(one, <two>, three) b"},
9377 );
9378
9379 editor.move_to_prev_snippet_tabstop(cx);
9380 assert(
9381 editor,
9382 cx,
9383 indoc! {"
9384 a.f(<one>, two, <three>) b
9385 a.f(<one>, two, <three>) b
9386 a.f(<one>, two, <three>) b"},
9387 );
9388
9389 assert!(editor.move_to_next_snippet_tabstop(cx));
9390 assert(
9391 editor,
9392 cx,
9393 indoc! {"
9394 a.f(one, <two>, three) b
9395 a.f(one, <two>, three) b
9396 a.f(one, <two>, three) b"},
9397 );
9398 assert!(editor.move_to_next_snippet_tabstop(cx));
9399 assert(
9400 editor,
9401 cx,
9402 indoc! {"
9403 a.f(one, two, three)<> b
9404 a.f(one, two, three)<> b
9405 a.f(one, two, three)<> b"},
9406 );
9407
9408 // As soon as the last tab stop is reached, snippet state is gone
9409 editor.move_to_prev_snippet_tabstop(cx);
9410 assert(
9411 editor,
9412 cx,
9413 indoc! {"
9414 a.f(one, two, three)<> b
9415 a.f(one, two, three)<> b
9416 a.f(one, two, three)<> b"},
9417 );
9418 });
9419 }
9420
9421 #[gpui::test]
9422 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9423 cx.foreground().forbid_parking();
9424
9425 let mut language = Language::new(
9426 LanguageConfig {
9427 name: "Rust".into(),
9428 path_suffixes: vec!["rs".to_string()],
9429 ..Default::default()
9430 },
9431 Some(tree_sitter_rust::language()),
9432 );
9433 let mut fake_servers = language
9434 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9435 capabilities: lsp::ServerCapabilities {
9436 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9437 ..Default::default()
9438 },
9439 ..Default::default()
9440 }))
9441 .await;
9442
9443 let fs = FakeFs::new(cx.background().clone());
9444 fs.insert_file("/file.rs", Default::default()).await;
9445
9446 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9447 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9448 let buffer = project
9449 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9450 .await
9451 .unwrap();
9452
9453 cx.foreground().start_waiting();
9454 let fake_server = fake_servers.next().await.unwrap();
9455
9456 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9457 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9458 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9459 assert!(cx.read(|cx| editor.is_dirty(cx)));
9460
9461 let save = cx.update(|cx| editor.save(project.clone(), cx));
9462 fake_server
9463 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9464 assert_eq!(
9465 params.text_document.uri,
9466 lsp::Url::from_file_path("/file.rs").unwrap()
9467 );
9468 assert_eq!(params.options.tab_size, 4);
9469 Ok(Some(vec![lsp::TextEdit::new(
9470 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9471 ", ".to_string(),
9472 )]))
9473 })
9474 .next()
9475 .await;
9476 cx.foreground().start_waiting();
9477 save.await.unwrap();
9478 assert_eq!(
9479 editor.read_with(cx, |editor, cx| editor.text(cx)),
9480 "one, two\nthree\n"
9481 );
9482 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9483
9484 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9485 assert!(cx.read(|cx| editor.is_dirty(cx)));
9486
9487 // Ensure we can still save even if formatting hangs.
9488 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9489 assert_eq!(
9490 params.text_document.uri,
9491 lsp::Url::from_file_path("/file.rs").unwrap()
9492 );
9493 futures::future::pending::<()>().await;
9494 unreachable!()
9495 });
9496 let save = cx.update(|cx| editor.save(project.clone(), cx));
9497 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9498 cx.foreground().start_waiting();
9499 save.await.unwrap();
9500 assert_eq!(
9501 editor.read_with(cx, |editor, cx| editor.text(cx)),
9502 "one\ntwo\nthree\n"
9503 );
9504 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9505
9506 // Set rust language override and assert overriden tabsize is sent to language server
9507 cx.update(|cx| {
9508 cx.update_global::<Settings, _, _>(|settings, _| {
9509 settings.language_overrides.insert(
9510 "Rust".into(),
9511 EditorSettings {
9512 tab_size: Some(8.try_into().unwrap()),
9513 ..Default::default()
9514 },
9515 );
9516 })
9517 });
9518
9519 let save = cx.update(|cx| editor.save(project.clone(), cx));
9520 fake_server
9521 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9522 assert_eq!(
9523 params.text_document.uri,
9524 lsp::Url::from_file_path("/file.rs").unwrap()
9525 );
9526 assert_eq!(params.options.tab_size, 8);
9527 Ok(Some(vec![]))
9528 })
9529 .next()
9530 .await;
9531 cx.foreground().start_waiting();
9532 save.await.unwrap();
9533 }
9534
9535 #[gpui::test]
9536 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9537 cx.foreground().forbid_parking();
9538
9539 let mut language = Language::new(
9540 LanguageConfig {
9541 name: "Rust".into(),
9542 path_suffixes: vec!["rs".to_string()],
9543 ..Default::default()
9544 },
9545 Some(tree_sitter_rust::language()),
9546 );
9547 let mut fake_servers = language
9548 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
9549 capabilities: lsp::ServerCapabilities {
9550 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9551 ..Default::default()
9552 },
9553 ..Default::default()
9554 }))
9555 .await;
9556
9557 let fs = FakeFs::new(cx.background().clone());
9558 fs.insert_file("/file.rs", Default::default()).await;
9559
9560 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9561 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9562 let buffer = project
9563 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9564 .await
9565 .unwrap();
9566
9567 cx.foreground().start_waiting();
9568 let fake_server = fake_servers.next().await.unwrap();
9569
9570 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9571 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9572 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9573 assert!(cx.read(|cx| editor.is_dirty(cx)));
9574
9575 let save = cx.update(|cx| editor.save(project.clone(), cx));
9576 fake_server
9577 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9578 assert_eq!(
9579 params.text_document.uri,
9580 lsp::Url::from_file_path("/file.rs").unwrap()
9581 );
9582 assert_eq!(params.options.tab_size, 4);
9583 Ok(Some(vec![lsp::TextEdit::new(
9584 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9585 ", ".to_string(),
9586 )]))
9587 })
9588 .next()
9589 .await;
9590 cx.foreground().start_waiting();
9591 save.await.unwrap();
9592 assert_eq!(
9593 editor.read_with(cx, |editor, cx| editor.text(cx)),
9594 "one, two\nthree\n"
9595 );
9596 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9597
9598 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9599 assert!(cx.read(|cx| editor.is_dirty(cx)));
9600
9601 // Ensure we can still save even if formatting hangs.
9602 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9603 move |params, _| async move {
9604 assert_eq!(
9605 params.text_document.uri,
9606 lsp::Url::from_file_path("/file.rs").unwrap()
9607 );
9608 futures::future::pending::<()>().await;
9609 unreachable!()
9610 },
9611 );
9612 let save = cx.update(|cx| editor.save(project.clone(), cx));
9613 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9614 cx.foreground().start_waiting();
9615 save.await.unwrap();
9616 assert_eq!(
9617 editor.read_with(cx, |editor, cx| editor.text(cx)),
9618 "one\ntwo\nthree\n"
9619 );
9620 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9621
9622 // Set rust language override and assert overriden tabsize is sent to language server
9623 cx.update(|cx| {
9624 cx.update_global::<Settings, _, _>(|settings, _| {
9625 settings.language_overrides.insert(
9626 "Rust".into(),
9627 EditorSettings {
9628 tab_size: Some(8.try_into().unwrap()),
9629 ..Default::default()
9630 },
9631 );
9632 })
9633 });
9634
9635 let save = cx.update(|cx| editor.save(project.clone(), cx));
9636 fake_server
9637 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9638 assert_eq!(
9639 params.text_document.uri,
9640 lsp::Url::from_file_path("/file.rs").unwrap()
9641 );
9642 assert_eq!(params.options.tab_size, 8);
9643 Ok(Some(vec![]))
9644 })
9645 .next()
9646 .await;
9647 cx.foreground().start_waiting();
9648 save.await.unwrap();
9649 }
9650
9651 #[gpui::test]
9652 async fn test_completion(cx: &mut gpui::TestAppContext) {
9653 let mut cx = EditorLspTestContext::new_rust(
9654 lsp::ServerCapabilities {
9655 completion_provider: Some(lsp::CompletionOptions {
9656 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9657 ..Default::default()
9658 }),
9659 ..Default::default()
9660 },
9661 cx,
9662 )
9663 .await;
9664
9665 cx.set_state(indoc! {"
9666 one|
9667 two
9668 three"});
9669 cx.simulate_keystroke(".");
9670 handle_completion_request(
9671 &mut cx,
9672 indoc! {"
9673 one.|<>
9674 two
9675 three"},
9676 vec!["first_completion", "second_completion"],
9677 )
9678 .await;
9679 cx.condition(|editor, _| editor.context_menu_visible())
9680 .await;
9681 let apply_additional_edits = cx.update_editor(|editor, cx| {
9682 editor.move_down(&MoveDown, cx);
9683 editor
9684 .confirm_completion(&ConfirmCompletion::default(), cx)
9685 .unwrap()
9686 });
9687 cx.assert_editor_state(indoc! {"
9688 one.second_completion|
9689 two
9690 three"});
9691
9692 handle_resolve_completion_request(
9693 &mut cx,
9694 Some((
9695 indoc! {"
9696 one.second_completion
9697 two
9698 three<>"},
9699 "\nadditional edit",
9700 )),
9701 )
9702 .await;
9703 apply_additional_edits.await.unwrap();
9704 cx.assert_editor_state(indoc! {"
9705 one.second_completion|
9706 two
9707 three
9708 additional edit"});
9709
9710 cx.set_state(indoc! {"
9711 one.second_completion
9712 two|
9713 three|
9714 additional edit"});
9715 cx.simulate_keystroke(" ");
9716 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9717 cx.simulate_keystroke("s");
9718 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9719
9720 cx.assert_editor_state(indoc! {"
9721 one.second_completion
9722 two s|
9723 three s|
9724 additional edit"});
9725 handle_completion_request(
9726 &mut cx,
9727 indoc! {"
9728 one.second_completion
9729 two s
9730 three <s|>
9731 additional edit"},
9732 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9733 )
9734 .await;
9735 cx.condition(|editor, _| editor.context_menu_visible())
9736 .await;
9737
9738 cx.simulate_keystroke("i");
9739
9740 handle_completion_request(
9741 &mut cx,
9742 indoc! {"
9743 one.second_completion
9744 two si
9745 three <si|>
9746 additional edit"},
9747 vec!["fourth_completion", "fifth_completion", "sixth_completion"],
9748 )
9749 .await;
9750 cx.condition(|editor, _| editor.context_menu_visible())
9751 .await;
9752
9753 let apply_additional_edits = cx.update_editor(|editor, cx| {
9754 editor
9755 .confirm_completion(&ConfirmCompletion::default(), cx)
9756 .unwrap()
9757 });
9758 cx.assert_editor_state(indoc! {"
9759 one.second_completion
9760 two sixth_completion|
9761 three sixth_completion|
9762 additional edit"});
9763
9764 handle_resolve_completion_request(&mut cx, None).await;
9765 apply_additional_edits.await.unwrap();
9766
9767 cx.update(|cx| {
9768 cx.update_global::<Settings, _, _>(|settings, _| {
9769 settings.show_completions_on_input = false;
9770 })
9771 });
9772 cx.set_state("editor|");
9773 cx.simulate_keystroke(".");
9774 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9775 cx.simulate_keystrokes(["c", "l", "o"]);
9776 cx.assert_editor_state("editor.clo|");
9777 assert!(cx.editor(|e, _| e.context_menu.is_none()));
9778 cx.update_editor(|editor, cx| {
9779 editor.show_completions(&ShowCompletions, cx);
9780 });
9781 handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
9782 cx.condition(|editor, _| editor.context_menu_visible())
9783 .await;
9784 let apply_additional_edits = cx.update_editor(|editor, cx| {
9785 editor
9786 .confirm_completion(&ConfirmCompletion::default(), cx)
9787 .unwrap()
9788 });
9789 cx.assert_editor_state("editor.close|");
9790 handle_resolve_completion_request(&mut cx, None).await;
9791 apply_additional_edits.await.unwrap();
9792
9793 // Handle completion request passing a marked string specifying where the completion
9794 // should be triggered from using '|' character, what range should be replaced, and what completions
9795 // should be returned using '<' and '>' to delimit the range
9796 async fn handle_completion_request<'a>(
9797 cx: &mut EditorLspTestContext<'a>,
9798 marked_string: &str,
9799 completions: Vec<&'static str>,
9800 ) {
9801 let complete_from_marker: TextRangeMarker = '|'.into();
9802 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9803 let (_, mut marked_ranges) = marked_text_ranges_by(
9804 marked_string,
9805 vec![complete_from_marker.clone(), replace_range_marker.clone()],
9806 );
9807
9808 let complete_from_position =
9809 cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9810 let replace_range =
9811 cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9812
9813 cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9814 let completions = completions.clone();
9815 async move {
9816 assert_eq!(params.text_document_position.text_document.uri, url.clone());
9817 assert_eq!(
9818 params.text_document_position.position,
9819 complete_from_position
9820 );
9821 Ok(Some(lsp::CompletionResponse::Array(
9822 completions
9823 .iter()
9824 .map(|completion_text| lsp::CompletionItem {
9825 label: completion_text.to_string(),
9826 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9827 range: replace_range.clone(),
9828 new_text: completion_text.to_string(),
9829 })),
9830 ..Default::default()
9831 })
9832 .collect(),
9833 )))
9834 }
9835 })
9836 .next()
9837 .await;
9838 }
9839
9840 async fn handle_resolve_completion_request<'a>(
9841 cx: &mut EditorLspTestContext<'a>,
9842 edit: Option<(&'static str, &'static str)>,
9843 ) {
9844 let edit = edit.map(|(marked_string, new_text)| {
9845 let replace_range_marker: TextRangeMarker = ('<', '>').into();
9846 let (_, mut marked_ranges) =
9847 marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]);
9848
9849 let replace_range = cx
9850 .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9851
9852 vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
9853 });
9854
9855 cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9856 let edit = edit.clone();
9857 async move {
9858 Ok(lsp::CompletionItem {
9859 additional_text_edits: edit,
9860 ..Default::default()
9861 })
9862 }
9863 })
9864 .next()
9865 .await;
9866 }
9867 }
9868
9869 #[gpui::test]
9870 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9871 cx.update(|cx| cx.set_global(Settings::test(cx)));
9872 let language = Arc::new(Language::new(
9873 LanguageConfig {
9874 line_comment: Some("// ".to_string()),
9875 ..Default::default()
9876 },
9877 Some(tree_sitter_rust::language()),
9878 ));
9879
9880 let text = "
9881 fn a() {
9882 //b();
9883 // c();
9884 // d();
9885 }
9886 "
9887 .unindent();
9888
9889 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9890 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9891 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9892
9893 view.update(cx, |editor, cx| {
9894 // If multiple selections intersect a line, the line is only
9895 // toggled once.
9896 editor.change_selections(None, cx, |s| {
9897 s.select_display_ranges([
9898 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9899 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9900 ])
9901 });
9902 editor.toggle_comments(&ToggleComments, cx);
9903 assert_eq!(
9904 editor.text(cx),
9905 "
9906 fn a() {
9907 b();
9908 c();
9909 d();
9910 }
9911 "
9912 .unindent()
9913 );
9914
9915 // The comment prefix is inserted at the same column for every line
9916 // in a selection.
9917 editor.change_selections(None, cx, |s| {
9918 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9919 });
9920 editor.toggle_comments(&ToggleComments, cx);
9921 assert_eq!(
9922 editor.text(cx),
9923 "
9924 fn a() {
9925 // b();
9926 // c();
9927 // d();
9928 }
9929 "
9930 .unindent()
9931 );
9932
9933 // If a selection ends at the beginning of a line, that line is not toggled.
9934 editor.change_selections(None, cx, |s| {
9935 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9936 });
9937 editor.toggle_comments(&ToggleComments, cx);
9938 assert_eq!(
9939 editor.text(cx),
9940 "
9941 fn a() {
9942 // b();
9943 c();
9944 // d();
9945 }
9946 "
9947 .unindent()
9948 );
9949 });
9950 }
9951
9952 #[gpui::test]
9953 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9954 cx.set_global(Settings::test(cx));
9955 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9956 let multibuffer = cx.add_model(|cx| {
9957 let mut multibuffer = MultiBuffer::new(0);
9958 multibuffer.push_excerpts(
9959 buffer.clone(),
9960 [
9961 ExcerptRange {
9962 context: Point::new(0, 0)..Point::new(0, 4),
9963 primary: None,
9964 },
9965 ExcerptRange {
9966 context: Point::new(1, 0)..Point::new(1, 4),
9967 primary: None,
9968 },
9969 ],
9970 cx,
9971 );
9972 multibuffer
9973 });
9974
9975 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9976
9977 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9978 view.update(cx, |view, cx| {
9979 assert_eq!(view.text(cx), "aaaa\nbbbb");
9980 view.change_selections(None, cx, |s| {
9981 s.select_ranges([
9982 Point::new(0, 0)..Point::new(0, 0),
9983 Point::new(1, 0)..Point::new(1, 0),
9984 ])
9985 });
9986
9987 view.handle_input("X", cx);
9988 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9989 assert_eq!(
9990 view.selections.ranges(cx),
9991 [
9992 Point::new(0, 1)..Point::new(0, 1),
9993 Point::new(1, 1)..Point::new(1, 1),
9994 ]
9995 )
9996 });
9997 }
9998
9999 #[gpui::test]
10000 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
10001 cx.set_global(Settings::test(cx));
10002 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
10003 [aaaa
10004 (bbbb]
10005 cccc)"});
10006 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
10007 context,
10008 primary: None,
10009 });
10010 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
10011 let multibuffer = cx.add_model(|cx| {
10012 let mut multibuffer = MultiBuffer::new(0);
10013 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10014 multibuffer
10015 });
10016
10017 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
10018 view.update(cx, |view, cx| {
10019 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
10020 aaaa
10021 b|bbb
10022 b|bb|b
10023 cccc"});
10024 assert_eq!(view.text(cx), expected_text);
10025 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
10026
10027 view.handle_input("X", cx);
10028
10029 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10030 aaaa
10031 bX|bbXb
10032 bX|bbX|b
10033 cccc"});
10034 assert_eq!(view.text(cx), expected_text);
10035 assert_eq!(view.selections.ranges(cx), expected_selections);
10036
10037 view.newline(&Newline, cx);
10038 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
10039 aaaa
10040 bX
10041 |bbX
10042 b
10043 bX
10044 |bbX
10045 |b
10046 cccc"});
10047 assert_eq!(view.text(cx), expected_text);
10048 assert_eq!(view.selections.ranges(cx), expected_selections);
10049 });
10050 }
10051
10052 #[gpui::test]
10053 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
10054 cx.set_global(Settings::test(cx));
10055 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10056 let mut excerpt1_id = None;
10057 let multibuffer = cx.add_model(|cx| {
10058 let mut multibuffer = MultiBuffer::new(0);
10059 excerpt1_id = multibuffer
10060 .push_excerpts(
10061 buffer.clone(),
10062 [
10063 ExcerptRange {
10064 context: Point::new(0, 0)..Point::new(1, 4),
10065 primary: None,
10066 },
10067 ExcerptRange {
10068 context: Point::new(1, 0)..Point::new(2, 4),
10069 primary: None,
10070 },
10071 ],
10072 cx,
10073 )
10074 .into_iter()
10075 .next();
10076 multibuffer
10077 });
10078 assert_eq!(
10079 multibuffer.read(cx).read(cx).text(),
10080 "aaaa\nbbbb\nbbbb\ncccc"
10081 );
10082 let (_, editor) = cx.add_window(Default::default(), |cx| {
10083 let mut editor = build_editor(multibuffer.clone(), cx);
10084 let snapshot = editor.snapshot(cx);
10085 editor.change_selections(None, cx, |s| {
10086 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10087 });
10088 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
10089 assert_eq!(
10090 editor.selections.ranges(cx),
10091 [
10092 Point::new(1, 3)..Point::new(1, 3),
10093 Point::new(2, 1)..Point::new(2, 1),
10094 ]
10095 );
10096 editor
10097 });
10098
10099 // Refreshing selections is a no-op when excerpts haven't changed.
10100 editor.update(cx, |editor, cx| {
10101 editor.change_selections(None, cx, |s| {
10102 s.refresh();
10103 });
10104 assert_eq!(
10105 editor.selections.ranges(cx),
10106 [
10107 Point::new(1, 3)..Point::new(1, 3),
10108 Point::new(2, 1)..Point::new(2, 1),
10109 ]
10110 );
10111 });
10112
10113 multibuffer.update(cx, |multibuffer, cx| {
10114 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10115 });
10116 editor.update(cx, |editor, cx| {
10117 // Removing an excerpt causes the first selection to become degenerate.
10118 assert_eq!(
10119 editor.selections.ranges(cx),
10120 [
10121 Point::new(0, 0)..Point::new(0, 0),
10122 Point::new(0, 1)..Point::new(0, 1)
10123 ]
10124 );
10125
10126 // Refreshing selections will relocate the first selection to the original buffer
10127 // location.
10128 editor.change_selections(None, cx, |s| {
10129 s.refresh();
10130 });
10131 assert_eq!(
10132 editor.selections.ranges(cx),
10133 [
10134 Point::new(0, 1)..Point::new(0, 1),
10135 Point::new(0, 3)..Point::new(0, 3)
10136 ]
10137 );
10138 assert!(editor.selections.pending_anchor().is_some());
10139 });
10140 }
10141
10142 #[gpui::test]
10143 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10144 cx.set_global(Settings::test(cx));
10145 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10146 let mut excerpt1_id = None;
10147 let multibuffer = cx.add_model(|cx| {
10148 let mut multibuffer = MultiBuffer::new(0);
10149 excerpt1_id = multibuffer
10150 .push_excerpts(
10151 buffer.clone(),
10152 [
10153 ExcerptRange {
10154 context: Point::new(0, 0)..Point::new(1, 4),
10155 primary: None,
10156 },
10157 ExcerptRange {
10158 context: Point::new(1, 0)..Point::new(2, 4),
10159 primary: None,
10160 },
10161 ],
10162 cx,
10163 )
10164 .into_iter()
10165 .next();
10166 multibuffer
10167 });
10168 assert_eq!(
10169 multibuffer.read(cx).read(cx).text(),
10170 "aaaa\nbbbb\nbbbb\ncccc"
10171 );
10172 let (_, editor) = cx.add_window(Default::default(), |cx| {
10173 let mut editor = build_editor(multibuffer.clone(), cx);
10174 let snapshot = editor.snapshot(cx);
10175 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10176 assert_eq!(
10177 editor.selections.ranges(cx),
10178 [Point::new(1, 3)..Point::new(1, 3)]
10179 );
10180 editor
10181 });
10182
10183 multibuffer.update(cx, |multibuffer, cx| {
10184 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10185 });
10186 editor.update(cx, |editor, cx| {
10187 assert_eq!(
10188 editor.selections.ranges(cx),
10189 [Point::new(0, 0)..Point::new(0, 0)]
10190 );
10191
10192 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10193 editor.change_selections(None, cx, |s| {
10194 s.refresh();
10195 });
10196 assert_eq!(
10197 editor.selections.ranges(cx),
10198 [Point::new(0, 3)..Point::new(0, 3)]
10199 );
10200 assert!(editor.selections.pending_anchor().is_some());
10201 });
10202 }
10203
10204 #[gpui::test]
10205 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10206 cx.update(|cx| cx.set_global(Settings::test(cx)));
10207 let language = Arc::new(
10208 Language::new(
10209 LanguageConfig {
10210 brackets: vec![
10211 BracketPair {
10212 start: "{".to_string(),
10213 end: "}".to_string(),
10214 close: true,
10215 newline: true,
10216 },
10217 BracketPair {
10218 start: "/* ".to_string(),
10219 end: " */".to_string(),
10220 close: true,
10221 newline: true,
10222 },
10223 ],
10224 ..Default::default()
10225 },
10226 Some(tree_sitter_rust::language()),
10227 )
10228 .with_indents_query("")
10229 .unwrap(),
10230 );
10231
10232 let text = concat!(
10233 "{ }\n", // Suppress rustfmt
10234 " x\n", //
10235 " /* */\n", //
10236 "x\n", //
10237 "{{} }\n", //
10238 );
10239
10240 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10241 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10242 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10243 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10244 .await;
10245
10246 view.update(cx, |view, cx| {
10247 view.change_selections(None, cx, |s| {
10248 s.select_display_ranges([
10249 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10250 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10251 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10252 ])
10253 });
10254 view.newline(&Newline, cx);
10255
10256 assert_eq!(
10257 view.buffer().read(cx).read(cx).text(),
10258 concat!(
10259 "{ \n", // Suppress rustfmt
10260 "\n", //
10261 "}\n", //
10262 " x\n", //
10263 " /* \n", //
10264 " \n", //
10265 " */\n", //
10266 "x\n", //
10267 "{{} \n", //
10268 "}\n", //
10269 )
10270 );
10271 });
10272 }
10273
10274 #[gpui::test]
10275 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10276 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10277
10278 cx.set_global(Settings::test(cx));
10279 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10280
10281 editor.update(cx, |editor, cx| {
10282 struct Type1;
10283 struct Type2;
10284
10285 let buffer = buffer.read(cx).snapshot(cx);
10286
10287 let anchor_range = |range: Range<Point>| {
10288 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10289 };
10290
10291 editor.highlight_background::<Type1>(
10292 vec![
10293 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10294 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10295 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10296 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10297 ],
10298 |_| Color::red(),
10299 cx,
10300 );
10301 editor.highlight_background::<Type2>(
10302 vec![
10303 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10304 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10305 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10306 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10307 ],
10308 |_| Color::green(),
10309 cx,
10310 );
10311
10312 let snapshot = editor.snapshot(cx);
10313 let mut highlighted_ranges = editor.background_highlights_in_range(
10314 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10315 &snapshot,
10316 cx.global::<Settings>().theme.as_ref(),
10317 );
10318 // Enforce a consistent ordering based on color without relying on the ordering of the
10319 // highlight's `TypeId` which is non-deterministic.
10320 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10321 assert_eq!(
10322 highlighted_ranges,
10323 &[
10324 (
10325 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10326 Color::green(),
10327 ),
10328 (
10329 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10330 Color::green(),
10331 ),
10332 (
10333 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10334 Color::red(),
10335 ),
10336 (
10337 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10338 Color::red(),
10339 ),
10340 ]
10341 );
10342 assert_eq!(
10343 editor.background_highlights_in_range(
10344 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10345 &snapshot,
10346 cx.global::<Settings>().theme.as_ref(),
10347 ),
10348 &[(
10349 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10350 Color::red(),
10351 )]
10352 );
10353 });
10354 }
10355
10356 #[gpui::test]
10357 fn test_following(cx: &mut gpui::MutableAppContext) {
10358 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10359
10360 cx.set_global(Settings::test(cx));
10361
10362 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10363 let (_, follower) = cx.add_window(
10364 WindowOptions {
10365 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10366 ..Default::default()
10367 },
10368 |cx| build_editor(buffer.clone(), cx),
10369 );
10370
10371 let pending_update = Rc::new(RefCell::new(None));
10372 follower.update(cx, {
10373 let update = pending_update.clone();
10374 |_, cx| {
10375 cx.subscribe(&leader, move |_, leader, event, cx| {
10376 leader
10377 .read(cx)
10378 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10379 })
10380 .detach();
10381 }
10382 });
10383
10384 // Update the selections only
10385 leader.update(cx, |leader, cx| {
10386 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10387 });
10388 follower.update(cx, |follower, cx| {
10389 follower
10390 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10391 .unwrap();
10392 });
10393 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10394
10395 // Update the scroll position only
10396 leader.update(cx, |leader, cx| {
10397 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10398 });
10399 follower.update(cx, |follower, cx| {
10400 follower
10401 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10402 .unwrap();
10403 });
10404 assert_eq!(
10405 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10406 vec2f(1.5, 3.5)
10407 );
10408
10409 // Update the selections and scroll position
10410 leader.update(cx, |leader, cx| {
10411 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10412 leader.request_autoscroll(Autoscroll::Newest, cx);
10413 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10414 });
10415 follower.update(cx, |follower, cx| {
10416 let initial_scroll_position = follower.scroll_position(cx);
10417 follower
10418 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10419 .unwrap();
10420 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10421 assert!(follower.autoscroll_request.is_some());
10422 });
10423 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10424
10425 // Creating a pending selection that precedes another selection
10426 leader.update(cx, |leader, cx| {
10427 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10428 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10429 });
10430 follower.update(cx, |follower, cx| {
10431 follower
10432 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10433 .unwrap();
10434 });
10435 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10436
10437 // Extend the pending selection so that it surrounds another selection
10438 leader.update(cx, |leader, cx| {
10439 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10440 });
10441 follower.update(cx, |follower, cx| {
10442 follower
10443 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10444 .unwrap();
10445 });
10446 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10447 }
10448
10449 #[test]
10450 fn test_combine_syntax_and_fuzzy_match_highlights() {
10451 let string = "abcdefghijklmnop";
10452 let syntax_ranges = [
10453 (
10454 0..3,
10455 HighlightStyle {
10456 color: Some(Color::red()),
10457 ..Default::default()
10458 },
10459 ),
10460 (
10461 4..8,
10462 HighlightStyle {
10463 color: Some(Color::green()),
10464 ..Default::default()
10465 },
10466 ),
10467 ];
10468 let match_indices = [4, 6, 7, 8];
10469 assert_eq!(
10470 combine_syntax_and_fuzzy_match_highlights(
10471 &string,
10472 Default::default(),
10473 syntax_ranges.into_iter(),
10474 &match_indices,
10475 ),
10476 &[
10477 (
10478 0..3,
10479 HighlightStyle {
10480 color: Some(Color::red()),
10481 ..Default::default()
10482 },
10483 ),
10484 (
10485 4..5,
10486 HighlightStyle {
10487 color: Some(Color::green()),
10488 weight: Some(fonts::Weight::BOLD),
10489 ..Default::default()
10490 },
10491 ),
10492 (
10493 5..6,
10494 HighlightStyle {
10495 color: Some(Color::green()),
10496 ..Default::default()
10497 },
10498 ),
10499 (
10500 6..8,
10501 HighlightStyle {
10502 color: Some(Color::green()),
10503 weight: Some(fonts::Weight::BOLD),
10504 ..Default::default()
10505 },
10506 ),
10507 (
10508 8..9,
10509 HighlightStyle {
10510 weight: Some(fonts::Weight::BOLD),
10511 ..Default::default()
10512 },
10513 ),
10514 ]
10515 );
10516 }
10517
10518 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10519 let point = DisplayPoint::new(row as u32, column as u32);
10520 point..point
10521 }
10522
10523 fn assert_selection_ranges(
10524 marked_text: &str,
10525 selection_marker_pairs: Vec<(char, char)>,
10526 view: &mut Editor,
10527 cx: &mut ViewContext<Editor>,
10528 ) {
10529 let snapshot = view.snapshot(cx).display_snapshot;
10530 let mut marker_chars = Vec::new();
10531 for (start, end) in selection_marker_pairs.iter() {
10532 marker_chars.push(*start);
10533 marker_chars.push(*end);
10534 }
10535 let (_, markers) = marked_text_by(marked_text, marker_chars);
10536 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10537 .iter()
10538 .map(|(start, end)| {
10539 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10540 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10541 start..end
10542 })
10543 .collect();
10544 assert_eq!(
10545 view.selections.display_ranges(cx),
10546 &asserted_ranges[..],
10547 "Assert selections are {}",
10548 marked_text
10549 );
10550 }
10551}
10552
10553trait RangeExt<T> {
10554 fn sorted(&self) -> Range<T>;
10555 fn to_inclusive(&self) -> RangeInclusive<T>;
10556}
10557
10558impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10559 fn sorted(&self) -> Self {
10560 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10561 }
10562
10563 fn to_inclusive(&self) -> RangeInclusive<T> {
10564 self.start.clone()..=self.end.clone()
10565 }
10566}
10567
10568trait RangeToAnchorExt {
10569 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10570}
10571
10572impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10573 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10574 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10575 }
10576}