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