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