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