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