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