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