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