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 mut 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() != 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 let mut delta = 0_isize;
3472 let mut start_offset = 0;
3473 for (i, selection) in selections.iter_mut().enumerate() {
3474 let to_insert;
3475 let entire_line;
3476 if let Some(clipboard_selection) = clipboard_selections.get(i) {
3477 let end_offset = start_offset + clipboard_selection.len;
3478 to_insert = &clipboard_text[start_offset..end_offset];
3479 entire_line = clipboard_selection.is_entire_line;
3480 start_offset = end_offset
3481 } else {
3482 to_insert = clipboard_text.as_str();
3483 entire_line = all_selections_were_entire_line;
3484 }
3485
3486 selection.start = (selection.start as isize + delta) as usize;
3487 selection.end = (selection.end as isize + delta) as usize;
3488
3489 this.buffer.update(cx, |buffer, cx| {
3490 // If the corresponding selection was empty when this slice of the
3491 // clipboard text was written, then the entire line containing the
3492 // selection was copied. If this selection is also currently empty,
3493 // then paste the line before the current line of the buffer.
3494 let range = if selection.is_empty() && entire_line {
3495 let column =
3496 selection.start.to_point(&buffer.read(cx)).column as usize;
3497 let line_start = selection.start - column;
3498 line_start..line_start
3499 } else {
3500 selection.start..selection.end
3501 };
3502
3503 delta += to_insert.len() as isize - range.len() as isize;
3504 buffer.edit([(range, to_insert)], cx);
3505 selection.start += to_insert.len();
3506 selection.end = selection.start;
3507 });
3508 }
3509 this.update_selections(selections, Some(Autoscroll::Fit), cx);
3510 } else {
3511 this.insert(&clipboard_text, cx);
3512 }
3513 }
3514 });
3515 }
3516
3517 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3518 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3519 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3520 self.set_selections(selections, None, true, cx);
3521 }
3522 self.request_autoscroll(Autoscroll::Fit, cx);
3523 cx.emit(Event::Edited);
3524 }
3525 }
3526
3527 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3528 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3529 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3530 {
3531 self.set_selections(selections, None, true, cx);
3532 }
3533 self.request_autoscroll(Autoscroll::Fit, cx);
3534 cx.emit(Event::Edited);
3535 }
3536 }
3537
3538 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3539 self.buffer
3540 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3541 }
3542
3543 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3544 self.move_selections(cx, |map, selection| {
3545 let cursor = if selection.is_empty() {
3546 movement::left(map, selection.start)
3547 } else {
3548 selection.start
3549 };
3550 selection.collapse_to(cursor, SelectionGoal::None);
3551 });
3552 }
3553
3554 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3555 self.move_selection_heads(cx, |map, head, _| {
3556 (movement::left(map, head), SelectionGoal::None)
3557 });
3558 }
3559
3560 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3561 self.move_selections(cx, |map, selection| {
3562 let cursor = if selection.is_empty() {
3563 movement::right(map, selection.end)
3564 } else {
3565 selection.end
3566 };
3567 selection.collapse_to(cursor, SelectionGoal::None)
3568 });
3569 }
3570
3571 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3572 self.move_selection_heads(cx, |map, head, _| {
3573 (movement::right(map, head), SelectionGoal::None)
3574 });
3575 }
3576
3577 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3578 if self.take_rename(true, cx).is_some() {
3579 return;
3580 }
3581
3582 if let Some(context_menu) = self.context_menu.as_mut() {
3583 if context_menu.select_prev(cx) {
3584 return;
3585 }
3586 }
3587
3588 if matches!(self.mode, EditorMode::SingleLine) {
3589 cx.propagate_action();
3590 return;
3591 }
3592
3593 self.move_selections(cx, |map, selection| {
3594 if !selection.is_empty() {
3595 selection.goal = SelectionGoal::None;
3596 }
3597 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3598 selection.collapse_to(cursor, goal);
3599 });
3600 }
3601
3602 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3603 self.move_selection_heads(cx, |map, head, goal| movement::up(map, head, goal, false))
3604 }
3605
3606 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3607 self.take_rename(true, cx);
3608
3609 if let Some(context_menu) = self.context_menu.as_mut() {
3610 if context_menu.select_next(cx) {
3611 return;
3612 }
3613 }
3614
3615 if matches!(self.mode, EditorMode::SingleLine) {
3616 cx.propagate_action();
3617 return;
3618 }
3619
3620 self.move_selections(cx, |map, selection| {
3621 if !selection.is_empty() {
3622 selection.goal = SelectionGoal::None;
3623 }
3624 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3625 selection.collapse_to(cursor, goal);
3626 });
3627 }
3628
3629 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3630 self.move_selection_heads(cx, |map, head, goal| movement::down(map, head, goal, false))
3631 }
3632
3633 pub fn move_to_previous_word_start(
3634 &mut self,
3635 _: &MoveToPreviousWordStart,
3636 cx: &mut ViewContext<Self>,
3637 ) {
3638 self.move_cursors(cx, |map, head, _| {
3639 (
3640 movement::previous_word_start(map, head),
3641 SelectionGoal::None,
3642 )
3643 });
3644 }
3645
3646 pub fn move_to_previous_subword_start(
3647 &mut self,
3648 _: &MoveToPreviousSubwordStart,
3649 cx: &mut ViewContext<Self>,
3650 ) {
3651 self.move_cursors(cx, |map, head, _| {
3652 (
3653 movement::previous_subword_start(map, head),
3654 SelectionGoal::None,
3655 )
3656 });
3657 }
3658
3659 pub fn select_to_previous_word_start(
3660 &mut self,
3661 _: &SelectToPreviousWordStart,
3662 cx: &mut ViewContext<Self>,
3663 ) {
3664 self.move_selection_heads(cx, |map, head, _| {
3665 (
3666 movement::previous_word_start(map, head),
3667 SelectionGoal::None,
3668 )
3669 });
3670 }
3671
3672 pub fn select_to_previous_subword_start(
3673 &mut self,
3674 _: &SelectToPreviousSubwordStart,
3675 cx: &mut ViewContext<Self>,
3676 ) {
3677 self.move_selection_heads(cx, |map, head, _| {
3678 (
3679 movement::previous_subword_start(map, head),
3680 SelectionGoal::None,
3681 )
3682 });
3683 }
3684
3685 pub fn delete_to_previous_word_start(
3686 &mut self,
3687 _: &DeleteToPreviousWordStart,
3688 cx: &mut ViewContext<Self>,
3689 ) {
3690 self.transact(cx, |this, cx| {
3691 this.move_selections(cx, |map, selection| {
3692 if selection.is_empty() {
3693 let cursor = movement::previous_word_start(map, selection.head());
3694 selection.set_head(cursor, SelectionGoal::None);
3695 }
3696 });
3697 this.insert("", cx);
3698 });
3699 }
3700
3701 pub fn delete_to_previous_subword_start(
3702 &mut self,
3703 _: &DeleteToPreviousSubwordStart,
3704 cx: &mut ViewContext<Self>,
3705 ) {
3706 self.transact(cx, |this, cx| {
3707 this.move_selections(cx, |map, selection| {
3708 if selection.is_empty() {
3709 let cursor = movement::previous_subword_start(map, selection.head());
3710 selection.set_head(cursor, SelectionGoal::None);
3711 }
3712 });
3713 this.insert("", cx);
3714 });
3715 }
3716
3717 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3718 self.move_cursors(cx, |map, head, _| {
3719 (movement::next_word_end(map, head), SelectionGoal::None)
3720 });
3721 }
3722
3723 pub fn move_to_next_subword_end(
3724 &mut self,
3725 _: &MoveToNextSubwordEnd,
3726 cx: &mut ViewContext<Self>,
3727 ) {
3728 self.move_cursors(cx, |map, head, _| {
3729 (movement::next_subword_end(map, head), SelectionGoal::None)
3730 });
3731 }
3732
3733 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3734 self.move_selection_heads(cx, |map, head, _| {
3735 (movement::next_word_end(map, head), SelectionGoal::None)
3736 });
3737 }
3738
3739 pub fn select_to_next_subword_end(
3740 &mut self,
3741 _: &SelectToNextSubwordEnd,
3742 cx: &mut ViewContext<Self>,
3743 ) {
3744 self.move_selection_heads(cx, |map, head, _| {
3745 (movement::next_subword_end(map, head), SelectionGoal::None)
3746 });
3747 }
3748
3749 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3750 self.transact(cx, |this, cx| {
3751 this.move_selections(cx, |map, selection| {
3752 if selection.is_empty() {
3753 let cursor = movement::next_word_end(map, selection.head());
3754 selection.set_head(cursor, SelectionGoal::None);
3755 }
3756 });
3757 this.insert("", cx);
3758 });
3759 }
3760
3761 pub fn delete_to_next_subword_end(
3762 &mut self,
3763 _: &DeleteToNextSubwordEnd,
3764 cx: &mut ViewContext<Self>,
3765 ) {
3766 self.transact(cx, |this, cx| {
3767 this.move_selections(cx, |map, selection| {
3768 if selection.is_empty() {
3769 let cursor = movement::next_subword_end(map, selection.head());
3770 selection.set_head(cursor, SelectionGoal::None);
3771 }
3772 });
3773 this.insert("", cx);
3774 });
3775 }
3776
3777 pub fn move_to_beginning_of_line(
3778 &mut self,
3779 _: &MoveToBeginningOfLine,
3780 cx: &mut ViewContext<Self>,
3781 ) {
3782 self.move_cursors(cx, |map, head, _| {
3783 (
3784 movement::line_beginning(map, head, true),
3785 SelectionGoal::None,
3786 )
3787 });
3788 }
3789
3790 pub fn select_to_beginning_of_line(
3791 &mut self,
3792 action: &SelectToBeginningOfLine,
3793 cx: &mut ViewContext<Self>,
3794 ) {
3795 self.move_selection_heads(cx, |map, head, _| {
3796 (
3797 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3798 SelectionGoal::None,
3799 )
3800 });
3801 }
3802
3803 pub fn delete_to_beginning_of_line(
3804 &mut self,
3805 _: &DeleteToBeginningOfLine,
3806 cx: &mut ViewContext<Self>,
3807 ) {
3808 self.transact(cx, |this, cx| {
3809 this.move_selections(cx, |_, selection| {
3810 selection.reversed = true;
3811 });
3812
3813 this.select_to_beginning_of_line(
3814 &SelectToBeginningOfLine {
3815 stop_at_soft_wraps: false,
3816 },
3817 cx,
3818 );
3819 this.backspace(&Backspace, cx);
3820 });
3821 }
3822
3823 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3824 self.move_cursors(cx, |map, head, _| {
3825 (movement::line_end(map, head, true), SelectionGoal::None)
3826 });
3827 }
3828
3829 pub fn select_to_end_of_line(
3830 &mut self,
3831 action: &SelectToEndOfLine,
3832 cx: &mut ViewContext<Self>,
3833 ) {
3834 self.move_selection_heads(cx, |map, head, _| {
3835 (
3836 movement::line_end(map, head, action.stop_at_soft_wraps),
3837 SelectionGoal::None,
3838 )
3839 });
3840 }
3841
3842 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3843 self.transact(cx, |this, cx| {
3844 this.select_to_end_of_line(
3845 &SelectToEndOfLine {
3846 stop_at_soft_wraps: false,
3847 },
3848 cx,
3849 );
3850 this.delete(&Delete, cx);
3851 });
3852 }
3853
3854 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
3855 self.transact(cx, |this, cx| {
3856 this.select_to_end_of_line(
3857 &SelectToEndOfLine {
3858 stop_at_soft_wraps: false,
3859 },
3860 cx,
3861 );
3862 this.cut(&Cut, cx);
3863 });
3864 }
3865
3866 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
3867 if matches!(self.mode, EditorMode::SingleLine) {
3868 cx.propagate_action();
3869 return;
3870 }
3871
3872 let selection = Selection {
3873 id: post_inc(&mut self.next_selection_id),
3874 start: 0,
3875 end: 0,
3876 reversed: false,
3877 goal: SelectionGoal::None,
3878 };
3879 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3880 }
3881
3882 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
3883 let mut selection = self.local_selections::<Point>(cx).last().unwrap().clone();
3884 selection.set_head(Point::zero(), SelectionGoal::None);
3885 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3886 }
3887
3888 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
3889 if matches!(self.mode, EditorMode::SingleLine) {
3890 cx.propagate_action();
3891 return;
3892 }
3893
3894 let cursor = self.buffer.read(cx).read(cx).len();
3895 let selection = Selection {
3896 id: post_inc(&mut self.next_selection_id),
3897 start: cursor,
3898 end: cursor,
3899 reversed: false,
3900 goal: SelectionGoal::None,
3901 };
3902 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3903 }
3904
3905 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
3906 self.nav_history = nav_history;
3907 }
3908
3909 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
3910 self.nav_history.as_ref()
3911 }
3912
3913 fn push_to_nav_history(
3914 &self,
3915 position: Anchor,
3916 new_position: Option<Point>,
3917 cx: &mut ViewContext<Self>,
3918 ) {
3919 if let Some(nav_history) = &self.nav_history {
3920 let buffer = self.buffer.read(cx).read(cx);
3921 let point = position.to_point(&buffer);
3922 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
3923 drop(buffer);
3924
3925 if let Some(new_position) = new_position {
3926 let row_delta = (new_position.row as i64 - point.row as i64).abs();
3927 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
3928 return;
3929 }
3930 }
3931
3932 nav_history.push(Some(NavigationData {
3933 cursor_anchor: position,
3934 cursor_position: point,
3935 scroll_position: self.scroll_position,
3936 scroll_top_anchor: self.scroll_top_anchor.clone(),
3937 scroll_top_row,
3938 }));
3939 }
3940 }
3941
3942 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
3943 let mut selection = self.local_selections::<usize>(cx).first().unwrap().clone();
3944 selection.set_head(self.buffer.read(cx).read(cx).len(), SelectionGoal::None);
3945 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3946 }
3947
3948 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
3949 let selection = Selection {
3950 id: post_inc(&mut self.next_selection_id),
3951 start: 0,
3952 end: self.buffer.read(cx).read(cx).len(),
3953 reversed: false,
3954 goal: SelectionGoal::None,
3955 };
3956 self.update_selections(vec![selection], None, cx);
3957 }
3958
3959 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
3960 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3961 let mut selections = self.local_selections::<Point>(cx);
3962 let max_point = display_map.buffer_snapshot.max_point();
3963 for selection in &mut selections {
3964 let rows = selection.spanned_rows(true, &display_map);
3965 selection.start = Point::new(rows.start, 0);
3966 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
3967 selection.reversed = false;
3968 }
3969 self.update_selections(selections, Some(Autoscroll::Fit), cx);
3970 }
3971
3972 pub fn split_selection_into_lines(
3973 &mut self,
3974 _: &SplitSelectionIntoLines,
3975 cx: &mut ViewContext<Self>,
3976 ) {
3977 let mut to_unfold = Vec::new();
3978 let mut new_selections = Vec::new();
3979 {
3980 let selections = self.local_selections::<Point>(cx);
3981 let buffer = self.buffer.read(cx).read(cx);
3982 for selection in selections {
3983 for row in selection.start.row..selection.end.row {
3984 let cursor = Point::new(row, buffer.line_len(row));
3985 new_selections.push(Selection {
3986 id: post_inc(&mut self.next_selection_id),
3987 start: cursor,
3988 end: cursor,
3989 reversed: false,
3990 goal: SelectionGoal::None,
3991 });
3992 }
3993 new_selections.push(Selection {
3994 id: selection.id,
3995 start: selection.end,
3996 end: selection.end,
3997 reversed: false,
3998 goal: SelectionGoal::None,
3999 });
4000 to_unfold.push(selection.start..selection.end);
4001 }
4002 }
4003 self.unfold_ranges(to_unfold, true, cx);
4004 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
4005 }
4006
4007 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4008 self.add_selection(true, cx);
4009 }
4010
4011 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4012 self.add_selection(false, cx);
4013 }
4014
4015 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4016 self.push_to_selection_history();
4017 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4018 let mut selections = self.local_selections::<Point>(cx);
4019 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4020 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4021 let range = oldest_selection.display_range(&display_map).sorted();
4022 let columns = cmp::min(range.start.column(), range.end.column())
4023 ..cmp::max(range.start.column(), range.end.column());
4024
4025 selections.clear();
4026 let mut stack = Vec::new();
4027 for row in range.start.row()..=range.end.row() {
4028 if let Some(selection) = self.build_columnar_selection(
4029 &display_map,
4030 row,
4031 &columns,
4032 oldest_selection.reversed,
4033 ) {
4034 stack.push(selection.id);
4035 selections.push(selection);
4036 }
4037 }
4038
4039 if above {
4040 stack.reverse();
4041 }
4042
4043 AddSelectionsState { above, stack }
4044 });
4045
4046 let last_added_selection = *state.stack.last().unwrap();
4047 let mut new_selections = Vec::new();
4048 if above == state.above {
4049 let end_row = if above {
4050 0
4051 } else {
4052 display_map.max_point().row()
4053 };
4054
4055 'outer: for selection in selections {
4056 if selection.id == last_added_selection {
4057 let range = selection.display_range(&display_map).sorted();
4058 debug_assert_eq!(range.start.row(), range.end.row());
4059 let mut row = range.start.row();
4060 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4061 {
4062 start..end
4063 } else {
4064 cmp::min(range.start.column(), range.end.column())
4065 ..cmp::max(range.start.column(), range.end.column())
4066 };
4067
4068 while row != end_row {
4069 if above {
4070 row -= 1;
4071 } else {
4072 row += 1;
4073 }
4074
4075 if let Some(new_selection) = self.build_columnar_selection(
4076 &display_map,
4077 row,
4078 &columns,
4079 selection.reversed,
4080 ) {
4081 state.stack.push(new_selection.id);
4082 if above {
4083 new_selections.push(new_selection);
4084 new_selections.push(selection);
4085 } else {
4086 new_selections.push(selection);
4087 new_selections.push(new_selection);
4088 }
4089
4090 continue 'outer;
4091 }
4092 }
4093 }
4094
4095 new_selections.push(selection);
4096 }
4097 } else {
4098 new_selections = selections;
4099 new_selections.retain(|s| s.id != last_added_selection);
4100 state.stack.pop();
4101 }
4102
4103 self.update_selections(new_selections, Some(Autoscroll::Newest), cx);
4104 if state.stack.len() > 1 {
4105 self.add_selections_state = Some(state);
4106 }
4107 }
4108
4109 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4110 self.push_to_selection_history();
4111 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4112 let buffer = &display_map.buffer_snapshot;
4113 let mut selections = self.local_selections::<usize>(cx);
4114 if let Some(mut select_next_state) = self.select_next_state.take() {
4115 let query = &select_next_state.query;
4116 if !select_next_state.done {
4117 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4118 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4119 let mut next_selected_range = None;
4120
4121 let bytes_after_last_selection =
4122 buffer.bytes_in_range(last_selection.end..buffer.len());
4123 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4124 let query_matches = query
4125 .stream_find_iter(bytes_after_last_selection)
4126 .map(|result| (last_selection.end, result))
4127 .chain(
4128 query
4129 .stream_find_iter(bytes_before_first_selection)
4130 .map(|result| (0, result)),
4131 );
4132 for (start_offset, query_match) in query_matches {
4133 let query_match = query_match.unwrap(); // can only fail due to I/O
4134 let offset_range =
4135 start_offset + query_match.start()..start_offset + query_match.end();
4136 let display_range = offset_range.start.to_display_point(&display_map)
4137 ..offset_range.end.to_display_point(&display_map);
4138
4139 if !select_next_state.wordwise
4140 || (!movement::is_inside_word(&display_map, display_range.start)
4141 && !movement::is_inside_word(&display_map, display_range.end))
4142 {
4143 next_selected_range = Some(offset_range);
4144 break;
4145 }
4146 }
4147
4148 if let Some(next_selected_range) = next_selected_range {
4149 if action.replace_newest {
4150 if let Some(newest_id) =
4151 selections.iter().max_by_key(|s| s.id).map(|s| s.id)
4152 {
4153 selections.retain(|s| s.id != newest_id);
4154 }
4155 }
4156 selections.push(Selection {
4157 id: post_inc(&mut self.next_selection_id),
4158 start: next_selected_range.start,
4159 end: next_selected_range.end,
4160 reversed: false,
4161 goal: SelectionGoal::None,
4162 });
4163 self.unfold_ranges([next_selected_range], false, cx);
4164 self.update_selections(selections, Some(Autoscroll::Newest), cx);
4165 } else {
4166 select_next_state.done = true;
4167 }
4168 }
4169
4170 self.select_next_state = Some(select_next_state);
4171 } else if selections.len() == 1 {
4172 let selection = selections.last_mut().unwrap();
4173 if selection.start == selection.end {
4174 let word_range = movement::surrounding_word(
4175 &display_map,
4176 selection.start.to_display_point(&display_map),
4177 );
4178 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4179 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4180 selection.goal = SelectionGoal::None;
4181 selection.reversed = false;
4182
4183 let query = buffer
4184 .text_for_range(selection.start..selection.end)
4185 .collect::<String>();
4186 let select_state = SelectNextState {
4187 query: AhoCorasick::new_auto_configured(&[query]),
4188 wordwise: true,
4189 done: false,
4190 };
4191 self.unfold_ranges([selection.start..selection.end], false, cx);
4192 self.update_selections(selections, Some(Autoscroll::Newest), cx);
4193 self.select_next_state = Some(select_state);
4194 } else {
4195 let query = buffer
4196 .text_for_range(selection.start..selection.end)
4197 .collect::<String>();
4198 self.select_next_state = Some(SelectNextState {
4199 query: AhoCorasick::new_auto_configured(&[query]),
4200 wordwise: false,
4201 done: false,
4202 });
4203 self.select_next(action, cx);
4204 }
4205 }
4206 }
4207
4208 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4209 self.transact(cx, |this, cx| {
4210 let mut selections = this.local_selections::<Point>(cx);
4211 let mut all_selection_lines_are_comments = true;
4212 let mut edit_ranges = Vec::new();
4213 let mut last_toggled_row = None;
4214 this.buffer.update(cx, |buffer, cx| {
4215 // TODO: Handle selections that cross excerpts
4216 for selection in &mut selections {
4217 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4218 // as that portion won't be used for detecting if a line is a comment.
4219 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4220 .language_at(selection.start, cx)
4221 .and_then(|l| l.line_comment_prefix())
4222 {
4223 prefix.into()
4224 } else {
4225 return;
4226 };
4227 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4228 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4229 edit_ranges.clear();
4230 let snapshot = buffer.snapshot(cx);
4231
4232 let end_row =
4233 if selection.end.row > selection.start.row && selection.end.column == 0 {
4234 selection.end.row
4235 } else {
4236 selection.end.row + 1
4237 };
4238
4239 for row in selection.start.row..end_row {
4240 // If multiple selections contain a given row, avoid processing that
4241 // row more than once.
4242 if last_toggled_row == Some(row) {
4243 continue;
4244 } else {
4245 last_toggled_row = Some(row);
4246 }
4247
4248 if snapshot.is_line_blank(row) {
4249 continue;
4250 }
4251
4252 let start = Point::new(row, snapshot.indent_column_for_line(row));
4253 let mut line_bytes = snapshot
4254 .bytes_in_range(start..snapshot.max_point())
4255 .flatten()
4256 .copied();
4257
4258 // If this line currently begins with the line comment prefix, then record
4259 // the range containing the prefix.
4260 if all_selection_lines_are_comments
4261 && line_bytes
4262 .by_ref()
4263 .take(comment_prefix.len())
4264 .eq(comment_prefix.bytes())
4265 {
4266 // Include any whitespace that matches the comment prefix.
4267 let matching_whitespace_len = line_bytes
4268 .zip(comment_prefix_whitespace.bytes())
4269 .take_while(|(a, b)| a == b)
4270 .count()
4271 as u32;
4272 let end = Point::new(
4273 row,
4274 start.column
4275 + comment_prefix.len() as u32
4276 + matching_whitespace_len,
4277 );
4278 edit_ranges.push(start..end);
4279 }
4280 // If this line does not begin with the line comment prefix, then record
4281 // the position where the prefix should be inserted.
4282 else {
4283 all_selection_lines_are_comments = false;
4284 edit_ranges.push(start..start);
4285 }
4286 }
4287
4288 if !edit_ranges.is_empty() {
4289 if all_selection_lines_are_comments {
4290 let empty_str: Arc<str> = "".into();
4291 buffer.edit(
4292 edit_ranges
4293 .iter()
4294 .cloned()
4295 .map(|range| (range, empty_str.clone())),
4296 cx,
4297 );
4298 } else {
4299 let min_column =
4300 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4301 let edits = edit_ranges.iter().map(|range| {
4302 let position = Point::new(range.start.row, min_column);
4303 (position..position, full_comment_prefix.clone())
4304 });
4305 buffer.edit(edits, cx);
4306 }
4307 }
4308 }
4309 });
4310
4311 this.update_selections(
4312 this.local_selections::<usize>(cx),
4313 Some(Autoscroll::Fit),
4314 cx,
4315 );
4316 });
4317 }
4318
4319 pub fn select_larger_syntax_node(
4320 &mut self,
4321 _: &SelectLargerSyntaxNode,
4322 cx: &mut ViewContext<Self>,
4323 ) {
4324 let old_selections = self.local_selections::<usize>(cx).into_boxed_slice();
4325 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4326 let buffer = self.buffer.read(cx).snapshot(cx);
4327
4328 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4329 let mut selected_larger_node = false;
4330 let new_selections = old_selections
4331 .iter()
4332 .map(|selection| {
4333 let old_range = selection.start..selection.end;
4334 let mut new_range = old_range.clone();
4335 while let Some(containing_range) =
4336 buffer.range_for_syntax_ancestor(new_range.clone())
4337 {
4338 new_range = containing_range;
4339 if !display_map.intersects_fold(new_range.start)
4340 && !display_map.intersects_fold(new_range.end)
4341 {
4342 break;
4343 }
4344 }
4345
4346 selected_larger_node |= new_range != old_range;
4347 Selection {
4348 id: selection.id,
4349 start: new_range.start,
4350 end: new_range.end,
4351 goal: SelectionGoal::None,
4352 reversed: selection.reversed,
4353 }
4354 })
4355 .collect::<Vec<_>>();
4356
4357 if selected_larger_node {
4358 stack.push(old_selections);
4359 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
4360 }
4361 self.select_larger_syntax_node_stack = stack;
4362 }
4363
4364 pub fn select_smaller_syntax_node(
4365 &mut self,
4366 _: &SelectSmallerSyntaxNode,
4367 cx: &mut ViewContext<Self>,
4368 ) {
4369 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4370 if let Some(selections) = stack.pop() {
4371 self.update_selections(selections.to_vec(), Some(Autoscroll::Fit), cx);
4372 }
4373 self.select_larger_syntax_node_stack = stack;
4374 }
4375
4376 pub fn move_to_enclosing_bracket(
4377 &mut self,
4378 _: &MoveToEnclosingBracket,
4379 cx: &mut ViewContext<Self>,
4380 ) {
4381 let mut selections = self.local_selections::<usize>(cx);
4382 let buffer = self.buffer.read(cx).snapshot(cx);
4383 for selection in &mut selections {
4384 if let Some((open_range, close_range)) =
4385 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4386 {
4387 let close_range = close_range.to_inclusive();
4388 let destination = if close_range.contains(&selection.start)
4389 && close_range.contains(&selection.end)
4390 {
4391 open_range.end
4392 } else {
4393 *close_range.start()
4394 };
4395 selection.start = destination;
4396 selection.end = destination;
4397 }
4398 }
4399
4400 self.update_selections(selections, Some(Autoscroll::Fit), cx);
4401 }
4402
4403 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4404 self.end_selection(cx);
4405 self.selection_history.mode = SelectionHistoryMode::Undoing;
4406 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4407 self.set_selections(entry.selections, None, true, cx);
4408 self.select_next_state = entry.select_next_state;
4409 self.add_selections_state = entry.add_selections_state;
4410 self.request_autoscroll(Autoscroll::Newest, cx);
4411 }
4412 self.selection_history.mode = SelectionHistoryMode::Normal;
4413 }
4414
4415 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4416 self.end_selection(cx);
4417 self.selection_history.mode = SelectionHistoryMode::Redoing;
4418 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4419 self.set_selections(entry.selections, None, true, cx);
4420 self.select_next_state = entry.select_next_state;
4421 self.add_selections_state = entry.add_selections_state;
4422 self.request_autoscroll(Autoscroll::Newest, cx);
4423 }
4424 self.selection_history.mode = SelectionHistoryMode::Normal;
4425 }
4426
4427 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4428 self.go_to_diagnostic(Direction::Next, cx)
4429 }
4430
4431 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4432 self.go_to_diagnostic(Direction::Prev, cx)
4433 }
4434
4435 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4436 let buffer = self.buffer.read(cx).snapshot(cx);
4437 let selection = self.newest_selection_with_snapshot::<usize>(&buffer);
4438 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4439 active_diagnostics
4440 .primary_range
4441 .to_offset(&buffer)
4442 .to_inclusive()
4443 });
4444 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4445 if active_primary_range.contains(&selection.head()) {
4446 *active_primary_range.end()
4447 } else {
4448 selection.head()
4449 }
4450 } else {
4451 selection.head()
4452 };
4453
4454 loop {
4455 let mut diagnostics = if direction == Direction::Prev {
4456 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4457 } else {
4458 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4459 };
4460 let group = diagnostics.find_map(|entry| {
4461 if entry.diagnostic.is_primary
4462 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4463 && !entry.range.is_empty()
4464 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4465 {
4466 Some((entry.range, entry.diagnostic.group_id))
4467 } else {
4468 None
4469 }
4470 });
4471
4472 if let Some((primary_range, group_id)) = group {
4473 self.activate_diagnostics(group_id, cx);
4474 self.update_selections(
4475 vec![Selection {
4476 id: selection.id,
4477 start: primary_range.start,
4478 end: primary_range.start,
4479 reversed: false,
4480 goal: SelectionGoal::None,
4481 }],
4482 Some(Autoscroll::Center),
4483 cx,
4484 );
4485 break;
4486 } else {
4487 // Cycle around to the start of the buffer, potentially moving back to the start of
4488 // the currently active diagnostic.
4489 active_primary_range.take();
4490 if direction == Direction::Prev {
4491 if search_start == buffer.len() {
4492 break;
4493 } else {
4494 search_start = buffer.len();
4495 }
4496 } else {
4497 if search_start == 0 {
4498 break;
4499 } else {
4500 search_start = 0;
4501 }
4502 }
4503 }
4504 }
4505 }
4506
4507 pub fn go_to_definition(
4508 workspace: &mut Workspace,
4509 _: &GoToDefinition,
4510 cx: &mut ViewContext<Workspace>,
4511 ) {
4512 let active_item = workspace.active_item(cx);
4513 let editor_handle = if let Some(editor) = active_item
4514 .as_ref()
4515 .and_then(|item| item.act_as::<Self>(cx))
4516 {
4517 editor
4518 } else {
4519 return;
4520 };
4521
4522 let editor = editor_handle.read(cx);
4523 let head = editor.newest_selection::<usize>(cx).head();
4524 let (buffer, head) =
4525 if let Some(text_anchor) = editor.buffer.read(cx).text_anchor_for_position(head, cx) {
4526 text_anchor
4527 } else {
4528 return;
4529 };
4530
4531 let project = workspace.project().clone();
4532 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4533 cx.spawn(|workspace, mut cx| async move {
4534 let definitions = definitions.await?;
4535 workspace.update(&mut cx, |workspace, cx| {
4536 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4537 for definition in definitions {
4538 let range = definition.range.to_offset(definition.buffer.read(cx));
4539
4540 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4541 target_editor_handle.update(cx, |target_editor, cx| {
4542 // When selecting a definition in a different buffer, disable the nav history
4543 // to avoid creating a history entry at the previous cursor location.
4544 if editor_handle != target_editor_handle {
4545 nav_history.borrow_mut().disable();
4546 }
4547 target_editor.select_ranges([range], Some(Autoscroll::Center), cx);
4548 nav_history.borrow_mut().enable();
4549 });
4550 }
4551 });
4552
4553 Ok::<(), anyhow::Error>(())
4554 })
4555 .detach_and_log_err(cx);
4556 }
4557
4558 pub fn find_all_references(
4559 workspace: &mut Workspace,
4560 _: &FindAllReferences,
4561 cx: &mut ViewContext<Workspace>,
4562 ) -> Option<Task<Result<()>>> {
4563 let active_item = workspace.active_item(cx)?;
4564 let editor_handle = active_item.act_as::<Self>(cx)?;
4565
4566 let editor = editor_handle.read(cx);
4567 let head = editor.newest_selection::<usize>(cx).head();
4568 let (buffer, head) = editor.buffer.read(cx).text_anchor_for_position(head, cx)?;
4569 let replica_id = editor.replica_id(cx);
4570
4571 let project = workspace.project().clone();
4572 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4573 Some(cx.spawn(|workspace, mut cx| async move {
4574 let mut locations = references.await?;
4575 if locations.is_empty() {
4576 return Ok(());
4577 }
4578
4579 locations.sort_by_key(|location| location.buffer.id());
4580 let mut locations = locations.into_iter().peekable();
4581 let mut ranges_to_highlight = Vec::new();
4582
4583 let excerpt_buffer = cx.add_model(|cx| {
4584 let mut symbol_name = None;
4585 let mut multibuffer = MultiBuffer::new(replica_id);
4586 while let Some(location) = locations.next() {
4587 let buffer = location.buffer.read(cx);
4588 let mut ranges_for_buffer = Vec::new();
4589 let range = location.range.to_offset(buffer);
4590 ranges_for_buffer.push(range.clone());
4591 if symbol_name.is_none() {
4592 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4593 }
4594
4595 while let Some(next_location) = locations.peek() {
4596 if next_location.buffer == location.buffer {
4597 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4598 locations.next();
4599 } else {
4600 break;
4601 }
4602 }
4603
4604 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4605 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4606 location.buffer.clone(),
4607 ranges_for_buffer,
4608 1,
4609 cx,
4610 ));
4611 }
4612 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4613 });
4614
4615 workspace.update(&mut cx, |workspace, cx| {
4616 let editor =
4617 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4618 editor.update(cx, |editor, cx| {
4619 editor.highlight_background::<Self>(
4620 ranges_to_highlight,
4621 |theme| theme.editor.highlighted_line_background,
4622 cx,
4623 );
4624 });
4625 workspace.add_item(Box::new(editor), cx);
4626 });
4627
4628 Ok(())
4629 }))
4630 }
4631
4632 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4633 use language::ToOffset as _;
4634
4635 let project = self.project.clone()?;
4636 let selection = self.newest_anchor_selection().clone();
4637 let (cursor_buffer, cursor_buffer_position) = self
4638 .buffer
4639 .read(cx)
4640 .text_anchor_for_position(selection.head(), cx)?;
4641 let (tail_buffer, _) = self
4642 .buffer
4643 .read(cx)
4644 .text_anchor_for_position(selection.tail(), cx)?;
4645 if tail_buffer != cursor_buffer {
4646 return None;
4647 }
4648
4649 let snapshot = cursor_buffer.read(cx).snapshot();
4650 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4651 let prepare_rename = project.update(cx, |project, cx| {
4652 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4653 });
4654
4655 Some(cx.spawn(|this, mut cx| async move {
4656 let rename_range = if let Some(range) = prepare_rename.await? {
4657 Some(range)
4658 } else {
4659 this.read_with(&cx, |this, cx| {
4660 let buffer = this.buffer.read(cx).snapshot(cx);
4661 let mut buffer_highlights = this
4662 .document_highlights_for_position(selection.head(), &buffer)
4663 .filter(|highlight| {
4664 highlight.start.excerpt_id() == selection.head().excerpt_id()
4665 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4666 });
4667 buffer_highlights
4668 .next()
4669 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4670 })
4671 };
4672 if let Some(rename_range) = rename_range {
4673 let rename_buffer_range = rename_range.to_offset(&snapshot);
4674 let cursor_offset_in_rename_range =
4675 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4676
4677 this.update(&mut cx, |this, cx| {
4678 this.take_rename(false, cx);
4679 let style = this.style(cx);
4680 let buffer = this.buffer.read(cx).read(cx);
4681 let cursor_offset = selection.head().to_offset(&buffer);
4682 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4683 let rename_end = rename_start + rename_buffer_range.len();
4684 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4685 let mut old_highlight_id = None;
4686 let old_name: Arc<str> = buffer
4687 .chunks(rename_start..rename_end, true)
4688 .map(|chunk| {
4689 if old_highlight_id.is_none() {
4690 old_highlight_id = chunk.syntax_highlight_id;
4691 }
4692 chunk.text
4693 })
4694 .collect::<String>()
4695 .into();
4696
4697 drop(buffer);
4698
4699 // Position the selection in the rename editor so that it matches the current selection.
4700 this.show_local_selections = false;
4701 let rename_editor = cx.add_view(|cx| {
4702 let mut editor = Editor::single_line(None, cx);
4703 if let Some(old_highlight_id) = old_highlight_id {
4704 editor.override_text_style =
4705 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4706 }
4707 editor
4708 .buffer
4709 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4710 editor.select_all(&SelectAll, cx);
4711 editor
4712 });
4713
4714 let ranges = this
4715 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4716 .into_iter()
4717 .flat_map(|(_, ranges)| ranges)
4718 .chain(
4719 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4720 .into_iter()
4721 .flat_map(|(_, ranges)| ranges),
4722 )
4723 .collect();
4724
4725 this.highlight_text::<Rename>(
4726 ranges,
4727 HighlightStyle {
4728 fade_out: Some(style.rename_fade),
4729 ..Default::default()
4730 },
4731 cx,
4732 );
4733 cx.focus(&rename_editor);
4734 let block_id = this.insert_blocks(
4735 [BlockProperties {
4736 position: range.start.clone(),
4737 height: 1,
4738 render: Arc::new({
4739 let editor = rename_editor.clone();
4740 move |cx: &BlockContext| {
4741 ChildView::new(editor.clone())
4742 .contained()
4743 .with_padding_left(cx.anchor_x)
4744 .boxed()
4745 }
4746 }),
4747 disposition: BlockDisposition::Below,
4748 }],
4749 cx,
4750 )[0];
4751 this.pending_rename = Some(RenameState {
4752 range,
4753 old_name,
4754 editor: rename_editor,
4755 block_id,
4756 });
4757 });
4758 }
4759
4760 Ok(())
4761 }))
4762 }
4763
4764 pub fn confirm_rename(
4765 workspace: &mut Workspace,
4766 _: &ConfirmRename,
4767 cx: &mut ViewContext<Workspace>,
4768 ) -> Option<Task<Result<()>>> {
4769 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4770
4771 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4772 let rename = editor.take_rename(false, cx)?;
4773 let buffer = editor.buffer.read(cx);
4774 let (start_buffer, start) =
4775 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4776 let (end_buffer, end) =
4777 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4778 if start_buffer == end_buffer {
4779 let new_name = rename.editor.read(cx).text(cx);
4780 Some((start_buffer, start..end, rename.old_name, new_name))
4781 } else {
4782 None
4783 }
4784 })?;
4785
4786 let rename = workspace.project().clone().update(cx, |project, cx| {
4787 project.perform_rename(
4788 buffer.clone(),
4789 range.start.clone(),
4790 new_name.clone(),
4791 true,
4792 cx,
4793 )
4794 });
4795
4796 Some(cx.spawn(|workspace, mut cx| async move {
4797 let project_transaction = rename.await?;
4798 Self::open_project_transaction(
4799 editor.clone(),
4800 workspace,
4801 project_transaction,
4802 format!("Rename: {} → {}", old_name, new_name),
4803 cx.clone(),
4804 )
4805 .await?;
4806
4807 editor.update(&mut cx, |editor, cx| {
4808 editor.refresh_document_highlights(cx);
4809 });
4810 Ok(())
4811 }))
4812 }
4813
4814 fn take_rename(
4815 &mut self,
4816 moving_cursor: bool,
4817 cx: &mut ViewContext<Self>,
4818 ) -> Option<RenameState> {
4819 let rename = self.pending_rename.take()?;
4820 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4821 self.clear_text_highlights::<Rename>(cx);
4822 self.show_local_selections = true;
4823
4824 if moving_cursor {
4825 let cursor_in_rename_editor =
4826 rename.editor.read(cx).newest_selection::<usize>(cx).head();
4827
4828 // Update the selection to match the position of the selection inside
4829 // the rename editor.
4830 let snapshot = self.buffer.read(cx).read(cx);
4831 let rename_range = rename.range.to_offset(&snapshot);
4832 let cursor_in_editor = snapshot
4833 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4834 .min(rename_range.end);
4835 drop(snapshot);
4836
4837 self.update_selections(
4838 vec![Selection {
4839 id: self.newest_anchor_selection().id,
4840 start: cursor_in_editor,
4841 end: cursor_in_editor,
4842 reversed: false,
4843 goal: SelectionGoal::None,
4844 }],
4845 None,
4846 cx,
4847 );
4848 }
4849
4850 Some(rename)
4851 }
4852
4853 #[cfg(any(test, feature = "test-support"))]
4854 pub fn pending_rename(&self) -> Option<&RenameState> {
4855 self.pending_rename.as_ref()
4856 }
4857
4858 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4859 if let Some(project) = self.project.clone() {
4860 self.buffer.update(cx, |multi_buffer, cx| {
4861 project.update(cx, |project, cx| {
4862 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4863 });
4864 })
4865 }
4866 }
4867
4868 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4869 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4870 let buffer = self.buffer.read(cx).snapshot(cx);
4871 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4872 let is_valid = buffer
4873 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
4874 .any(|entry| {
4875 entry.diagnostic.is_primary
4876 && !entry.range.is_empty()
4877 && entry.range.start == primary_range_start
4878 && entry.diagnostic.message == active_diagnostics.primary_message
4879 });
4880
4881 if is_valid != active_diagnostics.is_valid {
4882 active_diagnostics.is_valid = is_valid;
4883 let mut new_styles = HashMap::default();
4884 for (block_id, diagnostic) in &active_diagnostics.blocks {
4885 new_styles.insert(
4886 *block_id,
4887 diagnostic_block_renderer(diagnostic.clone(), is_valid),
4888 );
4889 }
4890 self.display_map
4891 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
4892 }
4893 }
4894 }
4895
4896 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
4897 self.dismiss_diagnostics(cx);
4898 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
4899 let buffer = self.buffer.read(cx).snapshot(cx);
4900
4901 let mut primary_range = None;
4902 let mut primary_message = None;
4903 let mut group_end = Point::zero();
4904 let diagnostic_group = buffer
4905 .diagnostic_group::<Point>(group_id)
4906 .map(|entry| {
4907 if entry.range.end > group_end {
4908 group_end = entry.range.end;
4909 }
4910 if entry.diagnostic.is_primary {
4911 primary_range = Some(entry.range.clone());
4912 primary_message = Some(entry.diagnostic.message.clone());
4913 }
4914 entry
4915 })
4916 .collect::<Vec<_>>();
4917 let primary_range = primary_range.unwrap();
4918 let primary_message = primary_message.unwrap();
4919 let primary_range =
4920 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
4921
4922 let blocks = display_map
4923 .insert_blocks(
4924 diagnostic_group.iter().map(|entry| {
4925 let diagnostic = entry.diagnostic.clone();
4926 let message_height = diagnostic.message.lines().count() as u8;
4927 BlockProperties {
4928 position: buffer.anchor_after(entry.range.start),
4929 height: message_height,
4930 render: diagnostic_block_renderer(diagnostic, true),
4931 disposition: BlockDisposition::Below,
4932 }
4933 }),
4934 cx,
4935 )
4936 .into_iter()
4937 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
4938 .collect();
4939
4940 Some(ActiveDiagnosticGroup {
4941 primary_range,
4942 primary_message,
4943 blocks,
4944 is_valid: true,
4945 })
4946 });
4947 }
4948
4949 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
4950 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
4951 self.display_map.update(cx, |display_map, cx| {
4952 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
4953 });
4954 cx.notify();
4955 }
4956 }
4957
4958 fn build_columnar_selection(
4959 &mut self,
4960 display_map: &DisplaySnapshot,
4961 row: u32,
4962 columns: &Range<u32>,
4963 reversed: bool,
4964 ) -> Option<Selection<Point>> {
4965 let is_empty = columns.start == columns.end;
4966 let line_len = display_map.line_len(row);
4967 if columns.start < line_len || (is_empty && columns.start == line_len) {
4968 let start = DisplayPoint::new(row, columns.start);
4969 let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
4970 Some(Selection {
4971 id: post_inc(&mut self.next_selection_id),
4972 start: start.to_point(display_map),
4973 end: end.to_point(display_map),
4974 reversed,
4975 goal: SelectionGoal::ColumnRange {
4976 start: columns.start,
4977 end: columns.end,
4978 },
4979 })
4980 } else {
4981 None
4982 }
4983 }
4984
4985 pub fn local_selections_in_range(
4986 &self,
4987 range: Range<Anchor>,
4988 display_map: &DisplaySnapshot,
4989 ) -> Vec<Selection<Point>> {
4990 let buffer = &display_map.buffer_snapshot;
4991
4992 let start_ix = match self
4993 .selections
4994 .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer))
4995 {
4996 Ok(ix) | Err(ix) => ix,
4997 };
4998 let end_ix = match self
4999 .selections
5000 .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer))
5001 {
5002 Ok(ix) => ix + 1,
5003 Err(ix) => ix,
5004 };
5005
5006 fn point_selection(
5007 selection: &Selection<Anchor>,
5008 buffer: &MultiBufferSnapshot,
5009 ) -> Selection<Point> {
5010 let start = selection.start.to_point(&buffer);
5011 let end = selection.end.to_point(&buffer);
5012 Selection {
5013 id: selection.id,
5014 start,
5015 end,
5016 reversed: selection.reversed,
5017 goal: selection.goal,
5018 }
5019 }
5020
5021 self.selections[start_ix..end_ix]
5022 .iter()
5023 .chain(
5024 self.pending_selection
5025 .as_ref()
5026 .map(|pending| &pending.selection),
5027 )
5028 .map(|s| point_selection(s, &buffer))
5029 .collect()
5030 }
5031
5032 pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
5033 where
5034 D: 'a + TextDimension + Ord + Sub<D, Output = D>,
5035 {
5036 let buffer = self.buffer.read(cx).snapshot(cx);
5037 let mut selections = self
5038 .resolve_selections::<D, _>(self.selections.iter(), &buffer)
5039 .peekable();
5040
5041 let mut pending_selection = self.pending_selection::<D>(&buffer);
5042
5043 iter::from_fn(move || {
5044 if let Some(pending) = pending_selection.as_mut() {
5045 while let Some(next_selection) = selections.peek() {
5046 if pending.start <= next_selection.end && pending.end >= next_selection.start {
5047 let next_selection = selections.next().unwrap();
5048 if next_selection.start < pending.start {
5049 pending.start = next_selection.start;
5050 }
5051 if next_selection.end > pending.end {
5052 pending.end = next_selection.end;
5053 }
5054 } else if next_selection.end < pending.start {
5055 return selections.next();
5056 } else {
5057 break;
5058 }
5059 }
5060
5061 pending_selection.take()
5062 } else {
5063 selections.next()
5064 }
5065 })
5066 .collect()
5067 }
5068
5069 fn resolve_selections<'a, D, I>(
5070 &self,
5071 selections: I,
5072 snapshot: &MultiBufferSnapshot,
5073 ) -> impl 'a + Iterator<Item = Selection<D>>
5074 where
5075 D: TextDimension + Ord + Sub<D, Output = D>,
5076 I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
5077 {
5078 let (to_summarize, selections) = selections.into_iter().tee();
5079 let mut summaries = snapshot
5080 .summaries_for_anchors::<D, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
5081 .into_iter();
5082 selections.map(move |s| Selection {
5083 id: s.id,
5084 start: summaries.next().unwrap(),
5085 end: summaries.next().unwrap(),
5086 reversed: s.reversed,
5087 goal: s.goal,
5088 })
5089 }
5090
5091 fn pending_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5092 &self,
5093 snapshot: &MultiBufferSnapshot,
5094 ) -> Option<Selection<D>> {
5095 self.pending_selection
5096 .as_ref()
5097 .map(|pending| self.resolve_selection(&pending.selection, &snapshot))
5098 }
5099
5100 fn resolve_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5101 &self,
5102 selection: &Selection<Anchor>,
5103 buffer: &MultiBufferSnapshot,
5104 ) -> Selection<D> {
5105 Selection {
5106 id: selection.id,
5107 start: selection.start.summary::<D>(&buffer),
5108 end: selection.end.summary::<D>(&buffer),
5109 reversed: selection.reversed,
5110 goal: selection.goal,
5111 }
5112 }
5113
5114 fn selection_count<'a>(&self) -> usize {
5115 let mut count = self.selections.len();
5116 if self.pending_selection.is_some() {
5117 count += 1;
5118 }
5119 count
5120 }
5121
5122 pub fn oldest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5123 &self,
5124 cx: &AppContext,
5125 ) -> Selection<D> {
5126 let snapshot = self.buffer.read(cx).read(cx);
5127 self.selections
5128 .iter()
5129 .min_by_key(|s| s.id)
5130 .map(|selection| self.resolve_selection(selection, &snapshot))
5131 .or_else(|| self.pending_selection(&snapshot))
5132 .unwrap()
5133 }
5134
5135 pub fn newest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5136 &self,
5137 cx: &AppContext,
5138 ) -> Selection<D> {
5139 self.resolve_selection(
5140 self.newest_anchor_selection(),
5141 &self.buffer.read(cx).read(cx),
5142 )
5143 }
5144
5145 pub fn newest_selection_with_snapshot<D: TextDimension + Ord + Sub<D, Output = D>>(
5146 &self,
5147 snapshot: &MultiBufferSnapshot,
5148 ) -> Selection<D> {
5149 self.resolve_selection(self.newest_anchor_selection(), snapshot)
5150 }
5151
5152 pub fn newest_anchor_selection(&self) -> &Selection<Anchor> {
5153 self.pending_selection
5154 .as_ref()
5155 .map(|s| &s.selection)
5156 .or_else(|| self.selections.iter().max_by_key(|s| s.id))
5157 .unwrap()
5158 }
5159
5160 pub fn update_selections<T>(
5161 &mut self,
5162 mut selections: Vec<Selection<T>>,
5163 autoscroll: Option<Autoscroll>,
5164 cx: &mut ViewContext<Self>,
5165 ) where
5166 T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
5167 {
5168 let buffer = self.buffer.read(cx).snapshot(cx);
5169 selections.sort_unstable_by_key(|s| s.start);
5170
5171 // Merge overlapping selections.
5172 let mut i = 1;
5173 while i < selections.len() {
5174 if selections[i - 1].end >= selections[i].start {
5175 let removed = selections.remove(i);
5176 if removed.start < selections[i - 1].start {
5177 selections[i - 1].start = removed.start;
5178 }
5179 if removed.end > selections[i - 1].end {
5180 selections[i - 1].end = removed.end;
5181 }
5182 } else {
5183 i += 1;
5184 }
5185 }
5186
5187 if let Some(autoscroll) = autoscroll {
5188 self.request_autoscroll(autoscroll, cx);
5189 }
5190
5191 self.set_selections(
5192 Arc::from_iter(selections.into_iter().map(|selection| {
5193 let end_bias = if selection.end > selection.start {
5194 Bias::Left
5195 } else {
5196 Bias::Right
5197 };
5198 Selection {
5199 id: selection.id,
5200 start: buffer.anchor_after(selection.start),
5201 end: buffer.anchor_at(selection.end, end_bias),
5202 reversed: selection.reversed,
5203 goal: selection.goal,
5204 }
5205 })),
5206 None,
5207 true,
5208 cx,
5209 );
5210 }
5211
5212 pub fn set_selections_from_remote(
5213 &mut self,
5214 mut selections: Vec<Selection<Anchor>>,
5215 cx: &mut ViewContext<Self>,
5216 ) {
5217 let buffer = self.buffer.read(cx);
5218 let buffer = buffer.read(cx);
5219 selections.sort_by(|a, b| {
5220 a.start
5221 .cmp(&b.start, &*buffer)
5222 .then_with(|| b.end.cmp(&a.end, &*buffer))
5223 });
5224
5225 // Merge overlapping selections
5226 let mut i = 1;
5227 while i < selections.len() {
5228 if selections[i - 1]
5229 .end
5230 .cmp(&selections[i].start, &*buffer)
5231 .is_ge()
5232 {
5233 let removed = selections.remove(i);
5234 if removed
5235 .start
5236 .cmp(&selections[i - 1].start, &*buffer)
5237 .is_lt()
5238 {
5239 selections[i - 1].start = removed.start;
5240 }
5241 if removed.end.cmp(&selections[i - 1].end, &*buffer).is_gt() {
5242 selections[i - 1].end = removed.end;
5243 }
5244 } else {
5245 i += 1;
5246 }
5247 }
5248
5249 drop(buffer);
5250 self.set_selections(selections.into(), None, false, cx);
5251 }
5252
5253 /// Compute new ranges for any selections that were located in excerpts that have
5254 /// since been removed.
5255 ///
5256 /// Returns a `HashMap` indicating which selections whose former head position
5257 /// was no longer present. The keys of the map are selection ids. The values are
5258 /// the id of the new excerpt where the head of the selection has been moved.
5259 pub fn refresh_selections(&mut self, cx: &mut ViewContext<Self>) -> HashMap<usize, ExcerptId> {
5260 let snapshot = self.buffer.read(cx).read(cx);
5261 let mut selections_with_lost_position = HashMap::default();
5262
5263 let mut pending_selection = self.pending_selection.take();
5264 if let Some(pending) = pending_selection.as_mut() {
5265 let anchors =
5266 snapshot.refresh_anchors([&pending.selection.start, &pending.selection.end]);
5267 let (_, start, kept_start) = anchors[0].clone();
5268 let (_, end, kept_end) = anchors[1].clone();
5269 let kept_head = if pending.selection.reversed {
5270 kept_start
5271 } else {
5272 kept_end
5273 };
5274 if !kept_head {
5275 selections_with_lost_position.insert(
5276 pending.selection.id,
5277 pending.selection.head().excerpt_id.clone(),
5278 );
5279 }
5280
5281 pending.selection.start = start;
5282 pending.selection.end = end;
5283 }
5284
5285 let anchors_with_status = snapshot.refresh_anchors(
5286 self.selections
5287 .iter()
5288 .flat_map(|selection| [&selection.start, &selection.end]),
5289 );
5290 self.selections = anchors_with_status
5291 .chunks(2)
5292 .map(|selection_anchors| {
5293 let (anchor_ix, start, kept_start) = selection_anchors[0].clone();
5294 let (_, end, kept_end) = selection_anchors[1].clone();
5295 let selection = &self.selections[anchor_ix / 2];
5296 let kept_head = if selection.reversed {
5297 kept_start
5298 } else {
5299 kept_end
5300 };
5301 if !kept_head {
5302 selections_with_lost_position
5303 .insert(selection.id, selection.head().excerpt_id.clone());
5304 }
5305
5306 Selection {
5307 id: selection.id,
5308 start,
5309 end,
5310 reversed: selection.reversed,
5311 goal: selection.goal,
5312 }
5313 })
5314 .collect();
5315 drop(snapshot);
5316
5317 let new_selections = self.local_selections::<usize>(cx);
5318 if !new_selections.is_empty() {
5319 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
5320 }
5321 self.pending_selection = pending_selection;
5322
5323 selections_with_lost_position
5324 }
5325
5326 fn set_selections(
5327 &mut self,
5328 selections: Arc<[Selection<Anchor>]>,
5329 pending_selection: Option<PendingSelection>,
5330 local: bool,
5331 cx: &mut ViewContext<Self>,
5332 ) {
5333 assert!(
5334 !selections.is_empty() || pending_selection.is_some(),
5335 "must have at least one selection"
5336 );
5337
5338 let old_cursor_position = self.newest_anchor_selection().head();
5339
5340 self.push_to_selection_history();
5341 self.selections = selections;
5342 self.pending_selection = pending_selection;
5343 if self.focused && self.leader_replica_id.is_none() {
5344 self.buffer.update(cx, |buffer, cx| {
5345 buffer.set_active_selections(&self.selections, cx)
5346 });
5347 }
5348
5349 let display_map = self
5350 .display_map
5351 .update(cx, |display_map, cx| display_map.snapshot(cx));
5352 let buffer = &display_map.buffer_snapshot;
5353 self.add_selections_state = None;
5354 self.select_next_state = None;
5355 self.select_larger_syntax_node_stack.clear();
5356 self.autoclose_stack.invalidate(&self.selections, &buffer);
5357 self.snippet_stack.invalidate(&self.selections, &buffer);
5358 self.take_rename(false, cx);
5359
5360 let new_cursor_position = self.newest_anchor_selection().head();
5361
5362 self.push_to_nav_history(
5363 old_cursor_position.clone(),
5364 Some(new_cursor_position.to_point(&buffer)),
5365 cx,
5366 );
5367
5368 if local {
5369 let completion_menu = match self.context_menu.as_mut() {
5370 Some(ContextMenu::Completions(menu)) => Some(menu),
5371 _ => {
5372 self.context_menu.take();
5373 None
5374 }
5375 };
5376
5377 if let Some(completion_menu) = completion_menu {
5378 let cursor_position = new_cursor_position.to_offset(&buffer);
5379 let (word_range, kind) =
5380 buffer.surrounding_word(completion_menu.initial_position.clone());
5381 if kind == Some(CharKind::Word)
5382 && word_range.to_inclusive().contains(&cursor_position)
5383 {
5384 let query = Self::completion_query(&buffer, cursor_position);
5385 cx.background()
5386 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
5387 self.show_completions(&ShowCompletions, cx);
5388 } else {
5389 self.hide_context_menu(cx);
5390 }
5391 }
5392
5393 if old_cursor_position.to_display_point(&display_map).row()
5394 != new_cursor_position.to_display_point(&display_map).row()
5395 {
5396 self.available_code_actions.take();
5397 }
5398 self.refresh_code_actions(cx);
5399 self.refresh_document_highlights(cx);
5400 }
5401
5402 self.pause_cursor_blinking(cx);
5403 cx.emit(Event::SelectionsChanged { local });
5404 }
5405
5406 fn push_to_selection_history(&mut self) {
5407 self.selection_history.push(SelectionHistoryEntry {
5408 selections: self.selections.clone(),
5409 select_next_state: self.select_next_state.clone(),
5410 add_selections_state: self.add_selections_state.clone(),
5411 });
5412 }
5413
5414 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5415 self.autoscroll_request = Some((autoscroll, true));
5416 cx.notify();
5417 }
5418
5419 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5420 self.autoscroll_request = Some((autoscroll, false));
5421 cx.notify();
5422 }
5423
5424 pub fn transact(
5425 &mut self,
5426 cx: &mut ViewContext<Self>,
5427 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5428 ) {
5429 self.start_transaction_at(Instant::now(), cx);
5430 update(self, cx);
5431 self.end_transaction_at(Instant::now(), cx);
5432 }
5433
5434 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5435 self.end_selection(cx);
5436 if let Some(tx_id) = self
5437 .buffer
5438 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5439 {
5440 self.selection_history
5441 .insert_transaction(tx_id, self.selections.clone());
5442 }
5443 }
5444
5445 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5446 if let Some(tx_id) = self
5447 .buffer
5448 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5449 {
5450 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5451 *end_selections = Some(self.selections.clone());
5452 } else {
5453 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5454 }
5455
5456 cx.emit(Event::Edited);
5457 }
5458 }
5459
5460 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5461 log::info!("Editor::page_up");
5462 }
5463
5464 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5465 log::info!("Editor::page_down");
5466 }
5467
5468 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5469 let mut fold_ranges = Vec::new();
5470
5471 let selections = self.local_selections::<Point>(cx);
5472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5473 for selection in selections {
5474 let range = selection.display_range(&display_map).sorted();
5475 let buffer_start_row = range.start.to_point(&display_map).row;
5476
5477 for row in (0..=range.end.row()).rev() {
5478 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5479 let fold_range = self.foldable_range_for_line(&display_map, row);
5480 if fold_range.end.row >= buffer_start_row {
5481 fold_ranges.push(fold_range);
5482 if row <= range.start.row() {
5483 break;
5484 }
5485 }
5486 }
5487 }
5488 }
5489
5490 self.fold_ranges(fold_ranges, cx);
5491 }
5492
5493 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5494 let selections = self.local_selections::<Point>(cx);
5495 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5496 let buffer = &display_map.buffer_snapshot;
5497 let ranges = selections
5498 .iter()
5499 .map(|s| {
5500 let range = s.display_range(&display_map).sorted();
5501 let mut start = range.start.to_point(&display_map);
5502 let mut end = range.end.to_point(&display_map);
5503 start.column = 0;
5504 end.column = buffer.line_len(end.row);
5505 start..end
5506 })
5507 .collect::<Vec<_>>();
5508 self.unfold_ranges(ranges, true, cx);
5509 }
5510
5511 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5512 let max_point = display_map.max_point();
5513 if display_row >= max_point.row() {
5514 false
5515 } else {
5516 let (start_indent, is_blank) = display_map.line_indent(display_row);
5517 if is_blank {
5518 false
5519 } else {
5520 for display_row in display_row + 1..=max_point.row() {
5521 let (indent, is_blank) = display_map.line_indent(display_row);
5522 if !is_blank {
5523 return indent > start_indent;
5524 }
5525 }
5526 false
5527 }
5528 }
5529 }
5530
5531 fn foldable_range_for_line(
5532 &self,
5533 display_map: &DisplaySnapshot,
5534 start_row: u32,
5535 ) -> Range<Point> {
5536 let max_point = display_map.max_point();
5537
5538 let (start_indent, _) = display_map.line_indent(start_row);
5539 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5540 let mut end = None;
5541 for row in start_row + 1..=max_point.row() {
5542 let (indent, is_blank) = display_map.line_indent(row);
5543 if !is_blank && indent <= start_indent {
5544 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5545 break;
5546 }
5547 }
5548
5549 let end = end.unwrap_or(max_point);
5550 return start.to_point(display_map)..end.to_point(display_map);
5551 }
5552
5553 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5554 let selections = self.local_selections::<Point>(cx);
5555 let ranges = selections.into_iter().map(|s| s.start..s.end);
5556 self.fold_ranges(ranges, cx);
5557 }
5558
5559 pub fn fold_ranges<T: ToOffset>(
5560 &mut self,
5561 ranges: impl IntoIterator<Item = Range<T>>,
5562 cx: &mut ViewContext<Self>,
5563 ) {
5564 let mut ranges = ranges.into_iter().peekable();
5565 if ranges.peek().is_some() {
5566 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5567 self.request_autoscroll(Autoscroll::Fit, cx);
5568 cx.notify();
5569 }
5570 }
5571
5572 pub fn unfold_ranges<T: ToOffset>(
5573 &mut self,
5574 ranges: impl IntoIterator<Item = Range<T>>,
5575 inclusive: bool,
5576 cx: &mut ViewContext<Self>,
5577 ) {
5578 let mut ranges = ranges.into_iter().peekable();
5579 if ranges.peek().is_some() {
5580 self.display_map
5581 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5582 self.request_autoscroll(Autoscroll::Fit, cx);
5583 cx.notify();
5584 }
5585 }
5586
5587 pub fn insert_blocks(
5588 &mut self,
5589 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5590 cx: &mut ViewContext<Self>,
5591 ) -> Vec<BlockId> {
5592 let blocks = self
5593 .display_map
5594 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5595 self.request_autoscroll(Autoscroll::Fit, cx);
5596 blocks
5597 }
5598
5599 pub fn replace_blocks(
5600 &mut self,
5601 blocks: HashMap<BlockId, RenderBlock>,
5602 cx: &mut ViewContext<Self>,
5603 ) {
5604 self.display_map
5605 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5606 self.request_autoscroll(Autoscroll::Fit, cx);
5607 }
5608
5609 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5610 self.display_map.update(cx, |display_map, cx| {
5611 display_map.remove_blocks(block_ids, cx)
5612 });
5613 }
5614
5615 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5616 self.display_map
5617 .update(cx, |map, cx| map.snapshot(cx))
5618 .longest_row()
5619 }
5620
5621 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5622 self.display_map
5623 .update(cx, |map, cx| map.snapshot(cx))
5624 .max_point()
5625 }
5626
5627 pub fn text(&self, cx: &AppContext) -> String {
5628 self.buffer.read(cx).read(cx).text()
5629 }
5630
5631 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5632 self.transact(cx, |this, cx| {
5633 this.buffer
5634 .read(cx)
5635 .as_singleton()
5636 .expect("you can only call set_text on editors for singleton buffers")
5637 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5638 });
5639 }
5640
5641 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5642 self.display_map
5643 .update(cx, |map, cx| map.snapshot(cx))
5644 .text()
5645 }
5646
5647 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5648 let language_name = self
5649 .buffer
5650 .read(cx)
5651 .as_singleton()
5652 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5653 .map(|l| l.name());
5654
5655 let settings = cx.global::<Settings>();
5656 let mode = self
5657 .soft_wrap_mode_override
5658 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5659 match mode {
5660 settings::SoftWrap::None => SoftWrap::None,
5661 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5662 settings::SoftWrap::PreferredLineLength => {
5663 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5664 }
5665 }
5666 }
5667
5668 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5669 self.soft_wrap_mode_override = Some(mode);
5670 cx.notify();
5671 }
5672
5673 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5674 self.display_map
5675 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5676 }
5677
5678 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5679 self.highlighted_rows = rows;
5680 }
5681
5682 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5683 self.highlighted_rows.clone()
5684 }
5685
5686 pub fn highlight_background<T: 'static>(
5687 &mut self,
5688 ranges: Vec<Range<Anchor>>,
5689 color_fetcher: fn(&Theme) -> Color,
5690 cx: &mut ViewContext<Self>,
5691 ) {
5692 self.background_highlights
5693 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5694 cx.notify();
5695 }
5696
5697 pub fn clear_background_highlights<T: 'static>(
5698 &mut self,
5699 cx: &mut ViewContext<Self>,
5700 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5701 cx.notify();
5702 self.background_highlights.remove(&TypeId::of::<T>())
5703 }
5704
5705 #[cfg(feature = "test-support")]
5706 pub fn all_background_highlights(
5707 &mut self,
5708 cx: &mut ViewContext<Self>,
5709 ) -> Vec<(Range<DisplayPoint>, Color)> {
5710 let snapshot = self.snapshot(cx);
5711 let buffer = &snapshot.buffer_snapshot;
5712 let start = buffer.anchor_before(0);
5713 let end = buffer.anchor_after(buffer.len());
5714 let theme = cx.global::<Settings>().theme.as_ref();
5715 self.background_highlights_in_range(start..end, &snapshot, theme)
5716 }
5717
5718 fn document_highlights_for_position<'a>(
5719 &'a self,
5720 position: Anchor,
5721 buffer: &'a MultiBufferSnapshot,
5722 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5723 let read_highlights = self
5724 .background_highlights
5725 .get(&TypeId::of::<DocumentHighlightRead>())
5726 .map(|h| &h.1);
5727 let write_highlights = self
5728 .background_highlights
5729 .get(&TypeId::of::<DocumentHighlightRead>())
5730 .map(|h| &h.1);
5731 let left_position = position.bias_left(buffer);
5732 let right_position = position.bias_right(buffer);
5733 read_highlights
5734 .into_iter()
5735 .chain(write_highlights)
5736 .flat_map(move |ranges| {
5737 let start_ix = match ranges.binary_search_by(|probe| {
5738 let cmp = probe.end.cmp(&left_position, &buffer);
5739 if cmp.is_ge() {
5740 Ordering::Greater
5741 } else {
5742 Ordering::Less
5743 }
5744 }) {
5745 Ok(i) | Err(i) => i,
5746 };
5747
5748 let right_position = right_position.clone();
5749 ranges[start_ix..]
5750 .iter()
5751 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5752 })
5753 }
5754
5755 pub fn background_highlights_in_range(
5756 &self,
5757 search_range: Range<Anchor>,
5758 display_snapshot: &DisplaySnapshot,
5759 theme: &Theme,
5760 ) -> Vec<(Range<DisplayPoint>, Color)> {
5761 let mut results = Vec::new();
5762 let buffer = &display_snapshot.buffer_snapshot;
5763 for (color_fetcher, ranges) in self.background_highlights.values() {
5764 let color = color_fetcher(theme);
5765 let start_ix = match ranges.binary_search_by(|probe| {
5766 let cmp = probe.end.cmp(&search_range.start, &buffer);
5767 if cmp.is_gt() {
5768 Ordering::Greater
5769 } else {
5770 Ordering::Less
5771 }
5772 }) {
5773 Ok(i) | Err(i) => i,
5774 };
5775 for range in &ranges[start_ix..] {
5776 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5777 break;
5778 }
5779 let start = range
5780 .start
5781 .to_point(buffer)
5782 .to_display_point(display_snapshot);
5783 let end = range
5784 .end
5785 .to_point(buffer)
5786 .to_display_point(display_snapshot);
5787 results.push((start..end, color))
5788 }
5789 }
5790 results
5791 }
5792
5793 pub fn highlight_text<T: 'static>(
5794 &mut self,
5795 ranges: Vec<Range<Anchor>>,
5796 style: HighlightStyle,
5797 cx: &mut ViewContext<Self>,
5798 ) {
5799 self.display_map.update(cx, |map, _| {
5800 map.highlight_text(TypeId::of::<T>(), ranges, style)
5801 });
5802 cx.notify();
5803 }
5804
5805 pub fn clear_text_highlights<T: 'static>(
5806 &mut self,
5807 cx: &mut ViewContext<Self>,
5808 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5809 cx.notify();
5810 self.display_map
5811 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5812 }
5813
5814 fn next_blink_epoch(&mut self) -> usize {
5815 self.blink_epoch += 1;
5816 self.blink_epoch
5817 }
5818
5819 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5820 if !self.focused {
5821 return;
5822 }
5823
5824 self.show_local_cursors = true;
5825 cx.notify();
5826
5827 let epoch = self.next_blink_epoch();
5828 cx.spawn(|this, mut cx| {
5829 let this = this.downgrade();
5830 async move {
5831 Timer::after(CURSOR_BLINK_INTERVAL).await;
5832 if let Some(this) = this.upgrade(&cx) {
5833 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5834 }
5835 }
5836 })
5837 .detach();
5838 }
5839
5840 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5841 if epoch == self.blink_epoch {
5842 self.blinking_paused = false;
5843 self.blink_cursors(epoch, cx);
5844 }
5845 }
5846
5847 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5848 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5849 self.show_local_cursors = !self.show_local_cursors;
5850 cx.notify();
5851
5852 let epoch = self.next_blink_epoch();
5853 cx.spawn(|this, mut cx| {
5854 let this = this.downgrade();
5855 async move {
5856 Timer::after(CURSOR_BLINK_INTERVAL).await;
5857 if let Some(this) = this.upgrade(&cx) {
5858 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5859 }
5860 }
5861 })
5862 .detach();
5863 }
5864 }
5865
5866 pub fn show_local_cursors(&self) -> bool {
5867 self.show_local_cursors && self.focused
5868 }
5869
5870 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5871 cx.notify();
5872 }
5873
5874 fn on_buffer_event(
5875 &mut self,
5876 _: ModelHandle<MultiBuffer>,
5877 event: &language::Event,
5878 cx: &mut ViewContext<Self>,
5879 ) {
5880 match event {
5881 language::Event::Edited => {
5882 self.refresh_active_diagnostics(cx);
5883 self.refresh_code_actions(cx);
5884 cx.emit(Event::BufferEdited);
5885 }
5886 language::Event::Reparsed => cx.emit(Event::Reparsed),
5887 language::Event::Dirtied => cx.emit(Event::Dirtied),
5888 language::Event::Saved => cx.emit(Event::Saved),
5889 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5890 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5891 language::Event::Closed => cx.emit(Event::Closed),
5892 language::Event::DiagnosticsUpdated => {
5893 self.refresh_active_diagnostics(cx);
5894 }
5895 _ => {}
5896 }
5897 }
5898
5899 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5900 cx.notify();
5901 }
5902
5903 pub fn set_searchable(&mut self, searchable: bool) {
5904 self.searchable = searchable;
5905 }
5906
5907 pub fn searchable(&self) -> bool {
5908 self.searchable
5909 }
5910
5911 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5912 let active_item = workspace.active_item(cx);
5913 let editor_handle = if let Some(editor) = active_item
5914 .as_ref()
5915 .and_then(|item| item.act_as::<Self>(cx))
5916 {
5917 editor
5918 } else {
5919 cx.propagate_action();
5920 return;
5921 };
5922
5923 let editor = editor_handle.read(cx);
5924 let buffer = editor.buffer.read(cx);
5925 if buffer.is_singleton() {
5926 cx.propagate_action();
5927 return;
5928 }
5929
5930 let mut new_selections_by_buffer = HashMap::default();
5931 for selection in editor.local_selections::<usize>(cx) {
5932 for (buffer, mut range) in
5933 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5934 {
5935 if selection.reversed {
5936 mem::swap(&mut range.start, &mut range.end);
5937 }
5938 new_selections_by_buffer
5939 .entry(buffer)
5940 .or_insert(Vec::new())
5941 .push(range)
5942 }
5943 }
5944
5945 editor_handle.update(cx, |editor, cx| {
5946 editor.push_to_nav_history(editor.newest_anchor_selection().head(), None, cx);
5947 });
5948 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5949 nav_history.borrow_mut().disable();
5950
5951 // We defer the pane interaction because we ourselves are a workspace item
5952 // and activating a new item causes the pane to call a method on us reentrantly,
5953 // which panics if we're on the stack.
5954 cx.defer(move |workspace, cx| {
5955 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5956 let editor = workspace.open_project_item::<Self>(buffer, cx);
5957 editor.update(cx, |editor, cx| {
5958 editor.select_ranges(ranges, Some(Autoscroll::Newest), cx);
5959 });
5960 }
5961
5962 nav_history.borrow_mut().enable();
5963 });
5964 }
5965}
5966
5967impl EditorSnapshot {
5968 pub fn is_focused(&self) -> bool {
5969 self.is_focused
5970 }
5971
5972 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5973 self.placeholder_text.as_ref()
5974 }
5975
5976 pub fn scroll_position(&self) -> Vector2F {
5977 compute_scroll_position(
5978 &self.display_snapshot,
5979 self.scroll_position,
5980 &self.scroll_top_anchor,
5981 )
5982 }
5983}
5984
5985impl Deref for EditorSnapshot {
5986 type Target = DisplaySnapshot;
5987
5988 fn deref(&self) -> &Self::Target {
5989 &self.display_snapshot
5990 }
5991}
5992
5993fn compute_scroll_position(
5994 snapshot: &DisplaySnapshot,
5995 mut scroll_position: Vector2F,
5996 scroll_top_anchor: &Anchor,
5997) -> Vector2F {
5998 if *scroll_top_anchor != Anchor::min() {
5999 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
6000 scroll_position.set_y(scroll_top + scroll_position.y());
6001 } else {
6002 scroll_position.set_y(0.);
6003 }
6004 scroll_position
6005}
6006
6007#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6008pub enum Event {
6009 Activate,
6010 BufferEdited,
6011 Edited,
6012 Reparsed,
6013 Blurred,
6014 Dirtied,
6015 Saved,
6016 TitleChanged,
6017 SelectionsChanged { local: bool },
6018 ScrollPositionChanged { local: bool },
6019 Closed,
6020}
6021
6022pub struct EditorFocused(pub ViewHandle<Editor>);
6023pub struct EditorBlurred(pub ViewHandle<Editor>);
6024pub struct EditorReleased(pub WeakViewHandle<Editor>);
6025
6026impl Entity for Editor {
6027 type Event = Event;
6028
6029 fn release(&mut self, cx: &mut MutableAppContext) {
6030 cx.emit_global(EditorReleased(self.handle.clone()));
6031 }
6032}
6033
6034impl View for Editor {
6035 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6036 let style = self.style(cx);
6037 self.display_map.update(cx, |map, cx| {
6038 map.set_font(style.text.font_id, style.text.font_size, cx)
6039 });
6040 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
6041 }
6042
6043 fn ui_name() -> &'static str {
6044 "Editor"
6045 }
6046
6047 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
6048 let focused_event = EditorFocused(cx.handle());
6049 cx.emit_global(focused_event);
6050 if let Some(rename) = self.pending_rename.as_ref() {
6051 cx.focus(&rename.editor);
6052 } else {
6053 self.focused = true;
6054 self.blink_cursors(self.blink_epoch, cx);
6055 self.buffer.update(cx, |buffer, cx| {
6056 buffer.finalize_last_transaction(cx);
6057 if self.leader_replica_id.is_none() {
6058 buffer.set_active_selections(&self.selections, cx);
6059 }
6060 });
6061 }
6062 }
6063
6064 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
6065 let blurred_event = EditorBlurred(cx.handle());
6066 cx.emit_global(blurred_event);
6067 self.focused = false;
6068 self.buffer
6069 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6070 self.hide_context_menu(cx);
6071 cx.emit(Event::Blurred);
6072 cx.notify();
6073 }
6074
6075 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6076 let mut context = Self::default_keymap_context();
6077 let mode = match self.mode {
6078 EditorMode::SingleLine => "single_line",
6079 EditorMode::AutoHeight { .. } => "auto_height",
6080 EditorMode::Full => "full",
6081 };
6082 context.map.insert("mode".into(), mode.into());
6083 if self.pending_rename.is_some() {
6084 context.set.insert("renaming".into());
6085 }
6086 match self.context_menu.as_ref() {
6087 Some(ContextMenu::Completions(_)) => {
6088 context.set.insert("showing_completions".into());
6089 }
6090 Some(ContextMenu::CodeActions(_)) => {
6091 context.set.insert("showing_code_actions".into());
6092 }
6093 None => {}
6094 }
6095
6096 for layer in self.keymap_context_layers.values() {
6097 context.extend(layer);
6098 }
6099
6100 context
6101 }
6102}
6103
6104fn build_style(
6105 settings: &Settings,
6106 get_field_editor_theme: Option<GetFieldEditorTheme>,
6107 override_text_style: Option<&OverrideTextStyle>,
6108 cx: &AppContext,
6109) -> EditorStyle {
6110 let font_cache = cx.font_cache();
6111
6112 let mut theme = settings.theme.editor.clone();
6113 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6114 let field_editor_theme = get_field_editor_theme(&settings.theme);
6115 theme.text_color = field_editor_theme.text.color;
6116 theme.selection = field_editor_theme.selection;
6117 theme.background = field_editor_theme
6118 .container
6119 .background_color
6120 .unwrap_or_default();
6121 EditorStyle {
6122 text: field_editor_theme.text,
6123 placeholder_text: field_editor_theme.placeholder_text,
6124 theme,
6125 }
6126 } else {
6127 let font_family_id = settings.buffer_font_family;
6128 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6129 let font_properties = Default::default();
6130 let font_id = font_cache
6131 .select_font(font_family_id, &font_properties)
6132 .unwrap();
6133 let font_size = settings.buffer_font_size;
6134 EditorStyle {
6135 text: TextStyle {
6136 color: settings.theme.editor.text_color,
6137 font_family_name,
6138 font_family_id,
6139 font_id,
6140 font_size,
6141 font_properties,
6142 underline: Default::default(),
6143 },
6144 placeholder_text: None,
6145 theme,
6146 }
6147 };
6148
6149 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6150 if let Some(highlighted) = style
6151 .text
6152 .clone()
6153 .highlight(highlight_style, font_cache)
6154 .log_err()
6155 {
6156 style.text = highlighted;
6157 }
6158 }
6159
6160 style
6161}
6162
6163trait SelectionExt {
6164 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6165 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6166 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6167 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6168 -> Range<u32>;
6169}
6170
6171impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6172 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6173 let start = self.start.to_point(buffer);
6174 let end = self.end.to_point(buffer);
6175 if self.reversed {
6176 end..start
6177 } else {
6178 start..end
6179 }
6180 }
6181
6182 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6183 let start = self.start.to_offset(buffer);
6184 let end = self.end.to_offset(buffer);
6185 if self.reversed {
6186 end..start
6187 } else {
6188 start..end
6189 }
6190 }
6191
6192 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6193 let start = self
6194 .start
6195 .to_point(&map.buffer_snapshot)
6196 .to_display_point(map);
6197 let end = self
6198 .end
6199 .to_point(&map.buffer_snapshot)
6200 .to_display_point(map);
6201 if self.reversed {
6202 end..start
6203 } else {
6204 start..end
6205 }
6206 }
6207
6208 fn spanned_rows(
6209 &self,
6210 include_end_if_at_line_start: bool,
6211 map: &DisplaySnapshot,
6212 ) -> Range<u32> {
6213 let start = self.start.to_point(&map.buffer_snapshot);
6214 let mut end = self.end.to_point(&map.buffer_snapshot);
6215 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6216 end.row -= 1;
6217 }
6218
6219 let buffer_start = map.prev_line_boundary(start).0;
6220 let buffer_end = map.next_line_boundary(end).0;
6221 buffer_start.row..buffer_end.row + 1
6222 }
6223}
6224
6225impl<T: InvalidationRegion> InvalidationStack<T> {
6226 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6227 where
6228 S: Clone + ToOffset,
6229 {
6230 while let Some(region) = self.last() {
6231 let all_selections_inside_invalidation_ranges =
6232 if selections.len() == region.ranges().len() {
6233 selections
6234 .iter()
6235 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6236 .all(|(selection, invalidation_range)| {
6237 let head = selection.head().to_offset(&buffer);
6238 invalidation_range.start <= head && invalidation_range.end >= head
6239 })
6240 } else {
6241 false
6242 };
6243
6244 if all_selections_inside_invalidation_ranges {
6245 break;
6246 } else {
6247 self.pop();
6248 }
6249 }
6250 }
6251}
6252
6253impl<T> Default for InvalidationStack<T> {
6254 fn default() -> Self {
6255 Self(Default::default())
6256 }
6257}
6258
6259impl<T> Deref for InvalidationStack<T> {
6260 type Target = Vec<T>;
6261
6262 fn deref(&self) -> &Self::Target {
6263 &self.0
6264 }
6265}
6266
6267impl<T> DerefMut for InvalidationStack<T> {
6268 fn deref_mut(&mut self) -> &mut Self::Target {
6269 &mut self.0
6270 }
6271}
6272
6273impl InvalidationRegion for BracketPairState {
6274 fn ranges(&self) -> &[Range<Anchor>] {
6275 &self.ranges
6276 }
6277}
6278
6279impl InvalidationRegion for SnippetState {
6280 fn ranges(&self) -> &[Range<Anchor>] {
6281 &self.ranges[self.active_index]
6282 }
6283}
6284
6285impl Deref for EditorStyle {
6286 type Target = theme::Editor;
6287
6288 fn deref(&self) -> &Self::Target {
6289 &self.theme
6290 }
6291}
6292
6293pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6294 let mut highlighted_lines = Vec::new();
6295 for line in diagnostic.message.lines() {
6296 highlighted_lines.push(highlight_diagnostic_message(line));
6297 }
6298
6299 Arc::new(move |cx: &BlockContext| {
6300 let settings = cx.global::<Settings>();
6301 let theme = &settings.theme.editor;
6302 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6303 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6304 Flex::column()
6305 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6306 Label::new(
6307 line.clone(),
6308 style.message.clone().with_font_size(font_size),
6309 )
6310 .with_highlights(highlights.clone())
6311 .contained()
6312 .with_margin_left(cx.anchor_x)
6313 .boxed()
6314 }))
6315 .aligned()
6316 .left()
6317 .boxed()
6318 })
6319}
6320
6321pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6322 let mut message_without_backticks = String::new();
6323 let mut prev_offset = 0;
6324 let mut inside_block = false;
6325 let mut highlights = Vec::new();
6326 for (match_ix, (offset, _)) in message
6327 .match_indices('`')
6328 .chain([(message.len(), "")])
6329 .enumerate()
6330 {
6331 message_without_backticks.push_str(&message[prev_offset..offset]);
6332 if inside_block {
6333 highlights.extend(prev_offset - match_ix..offset - match_ix);
6334 }
6335
6336 inside_block = !inside_block;
6337 prev_offset = offset + 1;
6338 }
6339
6340 (message_without_backticks, highlights)
6341}
6342
6343pub fn diagnostic_style(
6344 severity: DiagnosticSeverity,
6345 valid: bool,
6346 theme: &theme::Editor,
6347) -> DiagnosticStyle {
6348 match (severity, valid) {
6349 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6350 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6351 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6352 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6353 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6354 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6355 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6356 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6357 _ => theme.invalid_hint_diagnostic.clone(),
6358 }
6359}
6360
6361pub fn combine_syntax_and_fuzzy_match_highlights(
6362 text: &str,
6363 default_style: HighlightStyle,
6364 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6365 match_indices: &[usize],
6366) -> Vec<(Range<usize>, HighlightStyle)> {
6367 let mut result = Vec::new();
6368 let mut match_indices = match_indices.iter().copied().peekable();
6369
6370 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6371 {
6372 syntax_highlight.weight = None;
6373
6374 // Add highlights for any fuzzy match characters before the next
6375 // syntax highlight range.
6376 while let Some(&match_index) = match_indices.peek() {
6377 if match_index >= range.start {
6378 break;
6379 }
6380 match_indices.next();
6381 let end_index = char_ix_after(match_index, text);
6382 let mut match_style = default_style;
6383 match_style.weight = Some(fonts::Weight::BOLD);
6384 result.push((match_index..end_index, match_style));
6385 }
6386
6387 if range.start == usize::MAX {
6388 break;
6389 }
6390
6391 // Add highlights for any fuzzy match characters within the
6392 // syntax highlight range.
6393 let mut offset = range.start;
6394 while let Some(&match_index) = match_indices.peek() {
6395 if match_index >= range.end {
6396 break;
6397 }
6398
6399 match_indices.next();
6400 if match_index > offset {
6401 result.push((offset..match_index, syntax_highlight));
6402 }
6403
6404 let mut end_index = char_ix_after(match_index, text);
6405 while let Some(&next_match_index) = match_indices.peek() {
6406 if next_match_index == end_index && next_match_index < range.end {
6407 end_index = char_ix_after(next_match_index, text);
6408 match_indices.next();
6409 } else {
6410 break;
6411 }
6412 }
6413
6414 let mut match_style = syntax_highlight;
6415 match_style.weight = Some(fonts::Weight::BOLD);
6416 result.push((match_index..end_index, match_style));
6417 offset = end_index;
6418 }
6419
6420 if offset < range.end {
6421 result.push((offset..range.end, syntax_highlight));
6422 }
6423 }
6424
6425 fn char_ix_after(ix: usize, text: &str) -> usize {
6426 ix + text[ix..].chars().next().unwrap().len_utf8()
6427 }
6428
6429 result
6430}
6431
6432pub fn styled_runs_for_code_label<'a>(
6433 label: &'a CodeLabel,
6434 syntax_theme: &'a theme::SyntaxTheme,
6435) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6436 let fade_out = HighlightStyle {
6437 fade_out: Some(0.35),
6438 ..Default::default()
6439 };
6440
6441 let mut prev_end = label.filter_range.end;
6442 label
6443 .runs
6444 .iter()
6445 .enumerate()
6446 .flat_map(move |(ix, (range, highlight_id))| {
6447 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6448 style
6449 } else {
6450 return Default::default();
6451 };
6452 let mut muted_style = style.clone();
6453 muted_style.highlight(fade_out);
6454
6455 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6456 if range.start >= label.filter_range.end {
6457 if range.start > prev_end {
6458 runs.push((prev_end..range.start, fade_out));
6459 }
6460 runs.push((range.clone(), muted_style));
6461 } else if range.end <= label.filter_range.end {
6462 runs.push((range.clone(), style));
6463 } else {
6464 runs.push((range.start..label.filter_range.end, style));
6465 runs.push((label.filter_range.end..range.end, muted_style));
6466 }
6467 prev_end = cmp::max(prev_end, range.end);
6468
6469 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6470 runs.push((prev_end..label.text.len(), fade_out));
6471 }
6472
6473 runs
6474 })
6475}
6476
6477#[cfg(test)]
6478mod tests {
6479 use crate::test::{assert_text_with_selections, select_ranges};
6480
6481 use super::*;
6482 use gpui::{
6483 geometry::rect::RectF,
6484 platform::{WindowBounds, WindowOptions},
6485 };
6486 use indoc::indoc;
6487 use language::{FakeLspAdapter, LanguageConfig};
6488 use lsp::FakeLanguageServer;
6489 use project::FakeFs;
6490 use settings::LanguageOverride;
6491 use smol::stream::StreamExt;
6492 use std::{cell::RefCell, rc::Rc, time::Instant};
6493 use text::Point;
6494 use unindent::Unindent;
6495 use util::test::{marked_text_by, marked_text_ranges, sample_text};
6496 use workspace::{FollowableItem, ItemHandle};
6497
6498 #[gpui::test]
6499 fn test_edit_events(cx: &mut MutableAppContext) {
6500 cx.set_global(Settings::test(cx));
6501 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6502
6503 let events = Rc::new(RefCell::new(Vec::new()));
6504 let (_, editor1) = cx.add_window(Default::default(), {
6505 let events = events.clone();
6506 |cx| {
6507 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6508 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6509 events.borrow_mut().push(("editor1", *event));
6510 }
6511 })
6512 .detach();
6513 Editor::for_buffer(buffer.clone(), None, cx)
6514 }
6515 });
6516 let (_, editor2) = cx.add_window(Default::default(), {
6517 let events = events.clone();
6518 |cx| {
6519 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6520 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6521 events.borrow_mut().push(("editor2", *event));
6522 }
6523 })
6524 .detach();
6525 Editor::for_buffer(buffer.clone(), None, cx)
6526 }
6527 });
6528 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6529
6530 // Mutating editor 1 will emit an `Edited` event only for that editor.
6531 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6532 assert_eq!(
6533 mem::take(&mut *events.borrow_mut()),
6534 [
6535 ("editor1", Event::Edited),
6536 ("editor1", Event::BufferEdited),
6537 ("editor2", Event::BufferEdited),
6538 ("editor1", Event::Dirtied),
6539 ("editor2", Event::Dirtied)
6540 ]
6541 );
6542
6543 // Mutating editor 2 will emit an `Edited` event only for that editor.
6544 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6545 assert_eq!(
6546 mem::take(&mut *events.borrow_mut()),
6547 [
6548 ("editor2", Event::Edited),
6549 ("editor1", Event::BufferEdited),
6550 ("editor2", Event::BufferEdited),
6551 ]
6552 );
6553
6554 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6555 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6556 assert_eq!(
6557 mem::take(&mut *events.borrow_mut()),
6558 [
6559 ("editor1", Event::Edited),
6560 ("editor1", Event::BufferEdited),
6561 ("editor2", Event::BufferEdited),
6562 ]
6563 );
6564
6565 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6566 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6567 assert_eq!(
6568 mem::take(&mut *events.borrow_mut()),
6569 [
6570 ("editor1", Event::Edited),
6571 ("editor1", Event::BufferEdited),
6572 ("editor2", Event::BufferEdited),
6573 ]
6574 );
6575
6576 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6577 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6578 assert_eq!(
6579 mem::take(&mut *events.borrow_mut()),
6580 [
6581 ("editor2", Event::Edited),
6582 ("editor1", Event::BufferEdited),
6583 ("editor2", Event::BufferEdited),
6584 ]
6585 );
6586
6587 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6588 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6589 assert_eq!(
6590 mem::take(&mut *events.borrow_mut()),
6591 [
6592 ("editor2", Event::Edited),
6593 ("editor1", Event::BufferEdited),
6594 ("editor2", Event::BufferEdited),
6595 ]
6596 );
6597
6598 // No event is emitted when the mutation is a no-op.
6599 editor2.update(cx, |editor, cx| {
6600 editor.select_ranges([0..0], None, cx);
6601 editor.backspace(&Backspace, cx);
6602 });
6603 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6604 }
6605
6606 #[gpui::test]
6607 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6608 cx.set_global(Settings::test(cx));
6609 let mut now = Instant::now();
6610 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6611 let group_interval = buffer.read(cx).transaction_group_interval();
6612 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6613 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6614
6615 editor.update(cx, |editor, cx| {
6616 editor.start_transaction_at(now, cx);
6617 editor.select_ranges([2..4], None, cx);
6618 editor.insert("cd", cx);
6619 editor.end_transaction_at(now, cx);
6620 assert_eq!(editor.text(cx), "12cd56");
6621 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
6622
6623 editor.start_transaction_at(now, cx);
6624 editor.select_ranges([4..5], None, cx);
6625 editor.insert("e", cx);
6626 editor.end_transaction_at(now, cx);
6627 assert_eq!(editor.text(cx), "12cde6");
6628 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6629
6630 now += group_interval + Duration::from_millis(1);
6631 editor.select_ranges([2..2], None, cx);
6632
6633 // Simulate an edit in another editor
6634 buffer.update(cx, |buffer, cx| {
6635 buffer.start_transaction_at(now, cx);
6636 buffer.edit([(0..1, "a")], cx);
6637 buffer.edit([(1..1, "b")], cx);
6638 buffer.end_transaction_at(now, cx);
6639 });
6640
6641 assert_eq!(editor.text(cx), "ab2cde6");
6642 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
6643
6644 // Last transaction happened past the group interval in a different editor.
6645 // Undo it individually and don't restore selections.
6646 editor.undo(&Undo, cx);
6647 assert_eq!(editor.text(cx), "12cde6");
6648 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
6649
6650 // First two transactions happened within the group interval in this editor.
6651 // Undo them together and restore selections.
6652 editor.undo(&Undo, cx);
6653 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6654 assert_eq!(editor.text(cx), "123456");
6655 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
6656
6657 // Redo the first two transactions together.
6658 editor.redo(&Redo, cx);
6659 assert_eq!(editor.text(cx), "12cde6");
6660 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6661
6662 // Redo the last transaction on its own.
6663 editor.redo(&Redo, cx);
6664 assert_eq!(editor.text(cx), "ab2cde6");
6665 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
6666
6667 // Test empty transactions.
6668 editor.start_transaction_at(now, cx);
6669 editor.end_transaction_at(now, cx);
6670 editor.undo(&Undo, cx);
6671 assert_eq!(editor.text(cx), "12cde6");
6672 });
6673 }
6674
6675 #[gpui::test]
6676 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6677 cx.set_global(Settings::test(cx));
6678
6679 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6680 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6681 editor.update(cx, |view, cx| {
6682 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6683 });
6684 assert_eq!(
6685 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6686 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6687 );
6688
6689 editor.update(cx, |view, cx| {
6690 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6691 });
6692
6693 assert_eq!(
6694 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6695 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6696 );
6697
6698 editor.update(cx, |view, cx| {
6699 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6700 });
6701
6702 assert_eq!(
6703 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6704 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6705 );
6706
6707 editor.update(cx, |view, cx| {
6708 view.end_selection(cx);
6709 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6710 });
6711
6712 assert_eq!(
6713 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6714 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6715 );
6716
6717 editor.update(cx, |view, cx| {
6718 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6719 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6720 });
6721
6722 assert_eq!(
6723 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6724 [
6725 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6726 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6727 ]
6728 );
6729
6730 editor.update(cx, |view, cx| {
6731 view.end_selection(cx);
6732 });
6733
6734 assert_eq!(
6735 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6736 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6737 );
6738 }
6739
6740 #[gpui::test]
6741 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6742 cx.set_global(Settings::test(cx));
6743 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6744 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6745
6746 view.update(cx, |view, cx| {
6747 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6748 assert_eq!(
6749 view.selected_display_ranges(cx),
6750 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6751 );
6752 });
6753
6754 view.update(cx, |view, cx| {
6755 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6756 assert_eq!(
6757 view.selected_display_ranges(cx),
6758 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6759 );
6760 });
6761
6762 view.update(cx, |view, cx| {
6763 view.cancel(&Cancel, cx);
6764 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6765 assert_eq!(
6766 view.selected_display_ranges(cx),
6767 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6768 );
6769 });
6770 }
6771
6772 #[gpui::test]
6773 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6774 cx.set_global(Settings::test(cx));
6775 use workspace::Item;
6776 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6777 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6778
6779 cx.add_window(Default::default(), |cx| {
6780 let mut editor = build_editor(buffer.clone(), cx);
6781 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6782
6783 // Move the cursor a small distance.
6784 // Nothing is added to the navigation history.
6785 editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
6786 editor.select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx);
6787 assert!(nav_history.borrow_mut().pop_backward().is_none());
6788
6789 // Move the cursor a large distance.
6790 // The history can jump back to the previous position.
6791 editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx);
6792 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6793 editor.navigate(nav_entry.data.unwrap(), cx);
6794 assert_eq!(nav_entry.item.id(), cx.view_id());
6795 assert_eq!(
6796 editor.selected_display_ranges(cx),
6797 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6798 );
6799 assert!(nav_history.borrow_mut().pop_backward().is_none());
6800
6801 // Move the cursor a small distance via the mouse.
6802 // Nothing is added to the navigation history.
6803 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6804 editor.end_selection(cx);
6805 assert_eq!(
6806 editor.selected_display_ranges(cx),
6807 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6808 );
6809 assert!(nav_history.borrow_mut().pop_backward().is_none());
6810
6811 // Move the cursor a large distance via the mouse.
6812 // The history can jump back to the previous position.
6813 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6814 editor.end_selection(cx);
6815 assert_eq!(
6816 editor.selected_display_ranges(cx),
6817 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6818 );
6819 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6820 editor.navigate(nav_entry.data.unwrap(), cx);
6821 assert_eq!(nav_entry.item.id(), cx.view_id());
6822 assert_eq!(
6823 editor.selected_display_ranges(cx),
6824 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6825 );
6826 assert!(nav_history.borrow_mut().pop_backward().is_none());
6827
6828 // Set scroll position to check later
6829 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6830 let original_scroll_position = editor.scroll_position;
6831 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6832
6833 // Jump to the end of the document and adjust scroll
6834 editor.move_to_end(&MoveToEnd, cx);
6835 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6836 assert_ne!(editor.scroll_position, original_scroll_position);
6837 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6838
6839 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6840 editor.navigate(nav_entry.data.unwrap(), cx);
6841 assert_eq!(editor.scroll_position, original_scroll_position);
6842 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6843
6844 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6845 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6846 invalid_anchor.text_anchor.buffer_id = Some(999);
6847 let invalid_point = Point::new(9999, 0);
6848 editor.navigate(
6849 Box::new(NavigationData {
6850 cursor_anchor: invalid_anchor.clone(),
6851 cursor_position: invalid_point,
6852 scroll_top_anchor: invalid_anchor.clone(),
6853 scroll_top_row: invalid_point.row,
6854 scroll_position: Default::default(),
6855 }),
6856 cx,
6857 );
6858 assert_eq!(
6859 editor.selected_display_ranges(cx),
6860 &[editor.max_point(cx)..editor.max_point(cx)]
6861 );
6862 assert_eq!(
6863 editor.scroll_position(cx),
6864 vec2f(0., editor.max_point(cx).row() as f32)
6865 );
6866
6867 editor
6868 });
6869 }
6870
6871 #[gpui::test]
6872 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6873 cx.set_global(Settings::test(cx));
6874 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6875 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6876
6877 view.update(cx, |view, cx| {
6878 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6879 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6880 view.end_selection(cx);
6881
6882 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6883 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6884 view.end_selection(cx);
6885 assert_eq!(
6886 view.selected_display_ranges(cx),
6887 [
6888 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6889 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6890 ]
6891 );
6892 });
6893
6894 view.update(cx, |view, cx| {
6895 view.cancel(&Cancel, cx);
6896 assert_eq!(
6897 view.selected_display_ranges(cx),
6898 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6899 );
6900 });
6901
6902 view.update(cx, |view, cx| {
6903 view.cancel(&Cancel, cx);
6904 assert_eq!(
6905 view.selected_display_ranges(cx),
6906 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6907 );
6908 });
6909 }
6910
6911 #[gpui::test]
6912 fn test_fold(cx: &mut gpui::MutableAppContext) {
6913 cx.set_global(Settings::test(cx));
6914 let buffer = MultiBuffer::build_simple(
6915 &"
6916 impl Foo {
6917 // Hello!
6918
6919 fn a() {
6920 1
6921 }
6922
6923 fn b() {
6924 2
6925 }
6926
6927 fn c() {
6928 3
6929 }
6930 }
6931 "
6932 .unindent(),
6933 cx,
6934 );
6935 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6936
6937 view.update(cx, |view, cx| {
6938 view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx);
6939 view.fold(&Fold, cx);
6940 assert_eq!(
6941 view.display_text(cx),
6942 "
6943 impl Foo {
6944 // Hello!
6945
6946 fn a() {
6947 1
6948 }
6949
6950 fn b() {…
6951 }
6952
6953 fn c() {…
6954 }
6955 }
6956 "
6957 .unindent(),
6958 );
6959
6960 view.fold(&Fold, cx);
6961 assert_eq!(
6962 view.display_text(cx),
6963 "
6964 impl Foo {…
6965 }
6966 "
6967 .unindent(),
6968 );
6969
6970 view.unfold_lines(&UnfoldLines, cx);
6971 assert_eq!(
6972 view.display_text(cx),
6973 "
6974 impl Foo {
6975 // Hello!
6976
6977 fn a() {
6978 1
6979 }
6980
6981 fn b() {…
6982 }
6983
6984 fn c() {…
6985 }
6986 }
6987 "
6988 .unindent(),
6989 );
6990
6991 view.unfold_lines(&UnfoldLines, cx);
6992 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6993 });
6994 }
6995
6996 #[gpui::test]
6997 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6998 cx.set_global(Settings::test(cx));
6999 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7000 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7001
7002 buffer.update(cx, |buffer, cx| {
7003 buffer.edit(
7004 vec![
7005 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7006 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7007 ],
7008 cx,
7009 );
7010 });
7011
7012 view.update(cx, |view, cx| {
7013 assert_eq!(
7014 view.selected_display_ranges(cx),
7015 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7016 );
7017
7018 view.move_down(&MoveDown, cx);
7019 assert_eq!(
7020 view.selected_display_ranges(cx),
7021 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7022 );
7023
7024 view.move_right(&MoveRight, cx);
7025 assert_eq!(
7026 view.selected_display_ranges(cx),
7027 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7028 );
7029
7030 view.move_left(&MoveLeft, cx);
7031 assert_eq!(
7032 view.selected_display_ranges(cx),
7033 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7034 );
7035
7036 view.move_up(&MoveUp, cx);
7037 assert_eq!(
7038 view.selected_display_ranges(cx),
7039 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7040 );
7041
7042 view.move_to_end(&MoveToEnd, cx);
7043 assert_eq!(
7044 view.selected_display_ranges(cx),
7045 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7046 );
7047
7048 view.move_to_beginning(&MoveToBeginning, cx);
7049 assert_eq!(
7050 view.selected_display_ranges(cx),
7051 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7052 );
7053
7054 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx);
7055 view.select_to_beginning(&SelectToBeginning, cx);
7056 assert_eq!(
7057 view.selected_display_ranges(cx),
7058 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7059 );
7060
7061 view.select_to_end(&SelectToEnd, cx);
7062 assert_eq!(
7063 view.selected_display_ranges(cx),
7064 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7065 );
7066 });
7067 }
7068
7069 #[gpui::test]
7070 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7071 cx.set_global(Settings::test(cx));
7072 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7073 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7074
7075 assert_eq!('ⓐ'.len_utf8(), 3);
7076 assert_eq!('α'.len_utf8(), 2);
7077
7078 view.update(cx, |view, cx| {
7079 view.fold_ranges(
7080 vec![
7081 Point::new(0, 6)..Point::new(0, 12),
7082 Point::new(1, 2)..Point::new(1, 4),
7083 Point::new(2, 4)..Point::new(2, 8),
7084 ],
7085 cx,
7086 );
7087 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7088
7089 view.move_right(&MoveRight, cx);
7090 assert_eq!(
7091 view.selected_display_ranges(cx),
7092 &[empty_range(0, "ⓐ".len())]
7093 );
7094 view.move_right(&MoveRight, cx);
7095 assert_eq!(
7096 view.selected_display_ranges(cx),
7097 &[empty_range(0, "ⓐⓑ".len())]
7098 );
7099 view.move_right(&MoveRight, cx);
7100 assert_eq!(
7101 view.selected_display_ranges(cx),
7102 &[empty_range(0, "ⓐⓑ…".len())]
7103 );
7104
7105 view.move_down(&MoveDown, cx);
7106 assert_eq!(
7107 view.selected_display_ranges(cx),
7108 &[empty_range(1, "ab…".len())]
7109 );
7110 view.move_left(&MoveLeft, cx);
7111 assert_eq!(
7112 view.selected_display_ranges(cx),
7113 &[empty_range(1, "ab".len())]
7114 );
7115 view.move_left(&MoveLeft, cx);
7116 assert_eq!(
7117 view.selected_display_ranges(cx),
7118 &[empty_range(1, "a".len())]
7119 );
7120
7121 view.move_down(&MoveDown, cx);
7122 assert_eq!(
7123 view.selected_display_ranges(cx),
7124 &[empty_range(2, "α".len())]
7125 );
7126 view.move_right(&MoveRight, cx);
7127 assert_eq!(
7128 view.selected_display_ranges(cx),
7129 &[empty_range(2, "αβ".len())]
7130 );
7131 view.move_right(&MoveRight, cx);
7132 assert_eq!(
7133 view.selected_display_ranges(cx),
7134 &[empty_range(2, "αβ…".len())]
7135 );
7136 view.move_right(&MoveRight, cx);
7137 assert_eq!(
7138 view.selected_display_ranges(cx),
7139 &[empty_range(2, "αβ…ε".len())]
7140 );
7141
7142 view.move_up(&MoveUp, cx);
7143 assert_eq!(
7144 view.selected_display_ranges(cx),
7145 &[empty_range(1, "ab…e".len())]
7146 );
7147 view.move_up(&MoveUp, cx);
7148 assert_eq!(
7149 view.selected_display_ranges(cx),
7150 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7151 );
7152 view.move_left(&MoveLeft, cx);
7153 assert_eq!(
7154 view.selected_display_ranges(cx),
7155 &[empty_range(0, "ⓐⓑ…".len())]
7156 );
7157 view.move_left(&MoveLeft, cx);
7158 assert_eq!(
7159 view.selected_display_ranges(cx),
7160 &[empty_range(0, "ⓐⓑ".len())]
7161 );
7162 view.move_left(&MoveLeft, cx);
7163 assert_eq!(
7164 view.selected_display_ranges(cx),
7165 &[empty_range(0, "ⓐ".len())]
7166 );
7167 });
7168 }
7169
7170 #[gpui::test]
7171 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7172 cx.set_global(Settings::test(cx));
7173 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7174 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7175 view.update(cx, |view, cx| {
7176 view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx);
7177 view.move_down(&MoveDown, cx);
7178 assert_eq!(
7179 view.selected_display_ranges(cx),
7180 &[empty_range(1, "abcd".len())]
7181 );
7182
7183 view.move_down(&MoveDown, cx);
7184 assert_eq!(
7185 view.selected_display_ranges(cx),
7186 &[empty_range(2, "αβγ".len())]
7187 );
7188
7189 view.move_down(&MoveDown, cx);
7190 assert_eq!(
7191 view.selected_display_ranges(cx),
7192 &[empty_range(3, "abcd".len())]
7193 );
7194
7195 view.move_down(&MoveDown, cx);
7196 assert_eq!(
7197 view.selected_display_ranges(cx),
7198 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7199 );
7200
7201 view.move_up(&MoveUp, cx);
7202 assert_eq!(
7203 view.selected_display_ranges(cx),
7204 &[empty_range(3, "abcd".len())]
7205 );
7206
7207 view.move_up(&MoveUp, cx);
7208 assert_eq!(
7209 view.selected_display_ranges(cx),
7210 &[empty_range(2, "αβγ".len())]
7211 );
7212 });
7213 }
7214
7215 #[gpui::test]
7216 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7217 cx.set_global(Settings::test(cx));
7218 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7219 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7220 view.update(cx, |view, cx| {
7221 view.select_display_ranges(
7222 &[
7223 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7224 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7225 ],
7226 cx,
7227 );
7228 });
7229
7230 view.update(cx, |view, cx| {
7231 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7232 assert_eq!(
7233 view.selected_display_ranges(cx),
7234 &[
7235 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7236 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7237 ]
7238 );
7239 });
7240
7241 view.update(cx, |view, cx| {
7242 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7243 assert_eq!(
7244 view.selected_display_ranges(cx),
7245 &[
7246 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7247 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7248 ]
7249 );
7250 });
7251
7252 view.update(cx, |view, cx| {
7253 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7254 assert_eq!(
7255 view.selected_display_ranges(cx),
7256 &[
7257 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7258 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7259 ]
7260 );
7261 });
7262
7263 view.update(cx, |view, cx| {
7264 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7265 assert_eq!(
7266 view.selected_display_ranges(cx),
7267 &[
7268 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7269 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7270 ]
7271 );
7272 });
7273
7274 // Moving to the end of line again is a no-op.
7275 view.update(cx, |view, cx| {
7276 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7277 assert_eq!(
7278 view.selected_display_ranges(cx),
7279 &[
7280 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7281 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7282 ]
7283 );
7284 });
7285
7286 view.update(cx, |view, cx| {
7287 view.move_left(&MoveLeft, cx);
7288 view.select_to_beginning_of_line(
7289 &SelectToBeginningOfLine {
7290 stop_at_soft_wraps: true,
7291 },
7292 cx,
7293 );
7294 assert_eq!(
7295 view.selected_display_ranges(cx),
7296 &[
7297 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7298 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7299 ]
7300 );
7301 });
7302
7303 view.update(cx, |view, cx| {
7304 view.select_to_beginning_of_line(
7305 &SelectToBeginningOfLine {
7306 stop_at_soft_wraps: true,
7307 },
7308 cx,
7309 );
7310 assert_eq!(
7311 view.selected_display_ranges(cx),
7312 &[
7313 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7314 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7315 ]
7316 );
7317 });
7318
7319 view.update(cx, |view, cx| {
7320 view.select_to_beginning_of_line(
7321 &SelectToBeginningOfLine {
7322 stop_at_soft_wraps: true,
7323 },
7324 cx,
7325 );
7326 assert_eq!(
7327 view.selected_display_ranges(cx),
7328 &[
7329 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7330 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7331 ]
7332 );
7333 });
7334
7335 view.update(cx, |view, cx| {
7336 view.select_to_end_of_line(
7337 &SelectToEndOfLine {
7338 stop_at_soft_wraps: true,
7339 },
7340 cx,
7341 );
7342 assert_eq!(
7343 view.selected_display_ranges(cx),
7344 &[
7345 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7346 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7347 ]
7348 );
7349 });
7350
7351 view.update(cx, |view, cx| {
7352 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7353 assert_eq!(view.display_text(cx), "ab\n de");
7354 assert_eq!(
7355 view.selected_display_ranges(cx),
7356 &[
7357 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7358 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7359 ]
7360 );
7361 });
7362
7363 view.update(cx, |view, cx| {
7364 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7365 assert_eq!(view.display_text(cx), "\n");
7366 assert_eq!(
7367 view.selected_display_ranges(cx),
7368 &[
7369 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7370 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7371 ]
7372 );
7373 });
7374 }
7375
7376 #[gpui::test]
7377 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7378 cx.set_global(Settings::test(cx));
7379 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7380 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7381 view.update(cx, |view, cx| {
7382 view.select_display_ranges(
7383 &[
7384 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7385 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7386 ],
7387 cx,
7388 );
7389
7390 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7391 assert_selection_ranges(
7392 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7393 vec![('<', '>'), ('[', ']')],
7394 view,
7395 cx,
7396 );
7397
7398 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7399 assert_selection_ranges(
7400 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7401 vec![('<', '>'), ('[', ']')],
7402 view,
7403 cx,
7404 );
7405
7406 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7407 assert_selection_ranges(
7408 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7409 vec![('<', '>'), ('[', ']')],
7410 view,
7411 cx,
7412 );
7413
7414 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7415 assert_selection_ranges(
7416 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7417 vec![('<', '>'), ('[', ']')],
7418 view,
7419 cx,
7420 );
7421
7422 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7423 assert_selection_ranges(
7424 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7425 vec![('<', '>'), ('[', ']')],
7426 view,
7427 cx,
7428 );
7429
7430 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7431 assert_selection_ranges(
7432 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7433 vec![('<', '>'), ('[', ']')],
7434 view,
7435 cx,
7436 );
7437
7438 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7439 assert_selection_ranges(
7440 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7441 vec![('<', '>'), ('[', ']')],
7442 view,
7443 cx,
7444 );
7445
7446 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7447 assert_selection_ranges(
7448 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7449 vec![('<', '>'), ('[', ']')],
7450 view,
7451 cx,
7452 );
7453
7454 view.move_right(&MoveRight, cx);
7455 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7456 assert_selection_ranges(
7457 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7458 vec![('<', '>'), ('[', ']')],
7459 view,
7460 cx,
7461 );
7462
7463 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7464 assert_selection_ranges(
7465 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7466 vec![('<', '>'), ('[', ']')],
7467 view,
7468 cx,
7469 );
7470
7471 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7472 assert_selection_ranges(
7473 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7474 vec![('<', '>'), ('[', ']')],
7475 view,
7476 cx,
7477 );
7478 });
7479 }
7480
7481 #[gpui::test]
7482 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7483 cx.set_global(Settings::test(cx));
7484 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7485 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7486
7487 view.update(cx, |view, cx| {
7488 view.set_wrap_width(Some(140.), cx);
7489 assert_eq!(
7490 view.display_text(cx),
7491 "use one::{\n two::three::\n four::five\n};"
7492 );
7493
7494 view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx);
7495
7496 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7497 assert_eq!(
7498 view.selected_display_ranges(cx),
7499 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7500 );
7501
7502 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7503 assert_eq!(
7504 view.selected_display_ranges(cx),
7505 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7506 );
7507
7508 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7509 assert_eq!(
7510 view.selected_display_ranges(cx),
7511 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7512 );
7513
7514 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7515 assert_eq!(
7516 view.selected_display_ranges(cx),
7517 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7518 );
7519
7520 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7521 assert_eq!(
7522 view.selected_display_ranges(cx),
7523 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7524 );
7525
7526 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7527 assert_eq!(
7528 view.selected_display_ranges(cx),
7529 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7530 );
7531 });
7532 }
7533
7534 #[gpui::test]
7535 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7536 cx.set_global(Settings::test(cx));
7537 let (text, ranges) = marked_text_ranges("one [two three] four");
7538 let buffer = MultiBuffer::build_simple(&text, cx);
7539
7540 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7541
7542 editor.update(cx, |editor, cx| {
7543 editor.select_ranges(ranges, None, cx);
7544 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7545 assert_eq!(editor.text(cx), " four");
7546 });
7547 }
7548
7549 #[gpui::test]
7550 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7551 cx.set_global(Settings::test(cx));
7552 let buffer = MultiBuffer::build_simple("one two three four", cx);
7553 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7554
7555 view.update(cx, |view, cx| {
7556 view.select_display_ranges(
7557 &[
7558 // an empty selection - the preceding word fragment is deleted
7559 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7560 // characters selected - they are deleted
7561 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7562 ],
7563 cx,
7564 );
7565 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7566 });
7567
7568 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7569
7570 view.update(cx, |view, cx| {
7571 view.select_display_ranges(
7572 &[
7573 // an empty selection - the following word fragment is deleted
7574 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7575 // characters selected - they are deleted
7576 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7577 ],
7578 cx,
7579 );
7580 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7581 });
7582
7583 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7584 }
7585
7586 #[gpui::test]
7587 fn test_newline(cx: &mut gpui::MutableAppContext) {
7588 cx.set_global(Settings::test(cx));
7589 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7590 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7591
7592 view.update(cx, |view, cx| {
7593 view.select_display_ranges(
7594 &[
7595 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7596 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7597 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7598 ],
7599 cx,
7600 );
7601
7602 view.newline(&Newline, cx);
7603 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7604 });
7605 }
7606
7607 #[gpui::test]
7608 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7609 cx.set_global(Settings::test(cx));
7610 let buffer = MultiBuffer::build_simple(
7611 "
7612 a
7613 b(
7614 X
7615 )
7616 c(
7617 X
7618 )
7619 "
7620 .unindent()
7621 .as_str(),
7622 cx,
7623 );
7624
7625 let (_, editor) = cx.add_window(Default::default(), |cx| {
7626 let mut editor = build_editor(buffer.clone(), cx);
7627 editor.select_ranges(
7628 [
7629 Point::new(2, 4)..Point::new(2, 5),
7630 Point::new(5, 4)..Point::new(5, 5),
7631 ],
7632 None,
7633 cx,
7634 );
7635 editor
7636 });
7637
7638 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7639 buffer.update(cx, |buffer, cx| {
7640 buffer.edit(
7641 [
7642 (Point::new(1, 2)..Point::new(3, 0), ""),
7643 (Point::new(4, 2)..Point::new(6, 0), ""),
7644 ],
7645 cx,
7646 );
7647 assert_eq!(
7648 buffer.read(cx).text(),
7649 "
7650 a
7651 b()
7652 c()
7653 "
7654 .unindent()
7655 );
7656 });
7657
7658 editor.update(cx, |editor, cx| {
7659 assert_eq!(
7660 editor.selected_ranges(cx),
7661 &[
7662 Point::new(1, 2)..Point::new(1, 2),
7663 Point::new(2, 2)..Point::new(2, 2),
7664 ],
7665 );
7666
7667 editor.newline(&Newline, cx);
7668 assert_eq!(
7669 editor.text(cx),
7670 "
7671 a
7672 b(
7673 )
7674 c(
7675 )
7676 "
7677 .unindent()
7678 );
7679
7680 // The selections are moved after the inserted newlines
7681 assert_eq!(
7682 editor.selected_ranges(cx),
7683 &[
7684 Point::new(2, 0)..Point::new(2, 0),
7685 Point::new(4, 0)..Point::new(4, 0),
7686 ],
7687 );
7688 });
7689 }
7690
7691 #[gpui::test]
7692 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7693 cx.set_global(Settings::test(cx));
7694 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7695 let (_, editor) = cx.add_window(Default::default(), |cx| {
7696 let mut editor = build_editor(buffer.clone(), cx);
7697 editor.select_ranges([3..4, 11..12, 19..20], None, cx);
7698 editor
7699 });
7700
7701 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7702 buffer.update(cx, |buffer, cx| {
7703 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7704 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7705 });
7706
7707 editor.update(cx, |editor, cx| {
7708 assert_eq!(editor.selected_ranges(cx), &[2..2, 7..7, 12..12],);
7709
7710 editor.insert("Z", cx);
7711 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7712
7713 // The selections are moved after the inserted characters
7714 assert_eq!(editor.selected_ranges(cx), &[3..3, 9..9, 15..15],);
7715 });
7716 }
7717
7718 #[gpui::test]
7719 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
7720 cx.set_global(Settings::test(cx));
7721 let buffer = MultiBuffer::build_simple(
7722 indoc! {"
7723 one two
7724 three
7725 four"},
7726 cx,
7727 );
7728 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7729
7730 view.update(cx, |view, cx| {
7731 // two selections on the same line
7732 select_ranges(
7733 view,
7734 indoc! {"
7735 [one] [two]
7736 three
7737 four"},
7738 cx,
7739 );
7740
7741 // indent from mid-tabstop to full tabstop
7742 view.tab(&Tab, cx);
7743 assert_text_with_selections(
7744 view,
7745 indoc! {"
7746 [one] [two]
7747 three
7748 four"},
7749 cx,
7750 );
7751
7752 // outdent from 1 tabstop to 0 tabstops
7753 view.tab_prev(&TabPrev, cx);
7754 assert_text_with_selections(
7755 view,
7756 indoc! {"
7757 [one] [two]
7758 three
7759 four"},
7760 cx,
7761 );
7762
7763 // select across line ending
7764 select_ranges(
7765 view,
7766 indoc! {"
7767 one two
7768 t[hree
7769 ] four"},
7770 cx,
7771 );
7772
7773 // indent and outdent affect only the preceding line
7774 view.tab(&Tab, cx);
7775 assert_text_with_selections(
7776 view,
7777 indoc! {"
7778 one two
7779 t[hree
7780 ] four"},
7781 cx,
7782 );
7783 view.tab_prev(&TabPrev, cx);
7784 assert_text_with_selections(
7785 view,
7786 indoc! {"
7787 one two
7788 t[hree
7789 ] four"},
7790 cx,
7791 );
7792
7793 // Ensure that indenting/outdenting works when the cursor is at column 0.
7794 select_ranges(
7795 view,
7796 indoc! {"
7797 one two
7798 []three
7799 four"},
7800 cx,
7801 );
7802 view.tab(&Tab, cx);
7803 assert_text_with_selections(
7804 view,
7805 indoc! {"
7806 one two
7807 []three
7808 four"},
7809 cx,
7810 );
7811
7812 select_ranges(
7813 view,
7814 indoc! {"
7815 one two
7816 [] three
7817 four"},
7818 cx,
7819 );
7820 view.tab_prev(&TabPrev, cx);
7821 assert_text_with_selections(
7822 view,
7823 indoc! {"
7824 one two
7825 []three
7826 four"},
7827 cx,
7828 );
7829 });
7830 }
7831
7832 #[gpui::test]
7833 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7834 cx.set_global(
7835 Settings::test(cx)
7836 .with_overrides(
7837 "TOML",
7838 LanguageOverride {
7839 tab_size: Some(2),
7840 ..Default::default()
7841 },
7842 )
7843 .with_overrides(
7844 "Rust",
7845 LanguageOverride {
7846 tab_size: Some(4),
7847 ..Default::default()
7848 },
7849 ),
7850 );
7851 let toml_language = Arc::new(Language::new(
7852 LanguageConfig {
7853 name: "TOML".into(),
7854 ..Default::default()
7855 },
7856 None,
7857 ));
7858 let rust_language = Arc::new(Language::new(
7859 LanguageConfig {
7860 name: "Rust".into(),
7861 ..Default::default()
7862 },
7863 None,
7864 ));
7865
7866 let toml_buffer = cx
7867 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7868 let rust_buffer = cx.add_model(|cx| {
7869 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7870 });
7871 let multibuffer = cx.add_model(|cx| {
7872 let mut multibuffer = MultiBuffer::new(0);
7873 multibuffer.push_excerpts(
7874 toml_buffer.clone(),
7875 [Point::new(0, 0)..Point::new(2, 0)],
7876 cx,
7877 );
7878 multibuffer.push_excerpts(
7879 rust_buffer.clone(),
7880 [Point::new(0, 0)..Point::new(1, 0)],
7881 cx,
7882 );
7883 multibuffer
7884 });
7885
7886 cx.add_window(Default::default(), |cx| {
7887 let mut editor = build_editor(multibuffer, cx);
7888
7889 assert_eq!(
7890 editor.text(cx),
7891 indoc! {"
7892 a = 1
7893 b = 2
7894
7895 const c: usize = 3;
7896 "}
7897 );
7898
7899 select_ranges(
7900 &mut editor,
7901 indoc! {"
7902 [a] = 1
7903 b = 2
7904
7905 [const c:] usize = 3;
7906 "},
7907 cx,
7908 );
7909
7910 editor.tab(&Tab, cx);
7911 assert_text_with_selections(
7912 &mut editor,
7913 indoc! {"
7914 [a] = 1
7915 b = 2
7916
7917 [const c:] usize = 3;
7918 "},
7919 cx,
7920 );
7921 editor.tab_prev(&TabPrev, cx);
7922 assert_text_with_selections(
7923 &mut editor,
7924 indoc! {"
7925 [a] = 1
7926 b = 2
7927
7928 [const c:] usize = 3;
7929 "},
7930 cx,
7931 );
7932
7933 editor
7934 });
7935 }
7936
7937 #[gpui::test]
7938 fn test_backspace(cx: &mut gpui::MutableAppContext) {
7939 cx.set_global(Settings::test(cx));
7940 let (_, view) = cx.add_window(Default::default(), |cx| {
7941 build_editor(MultiBuffer::build_simple("", cx), cx)
7942 });
7943
7944 view.update(cx, |view, cx| {
7945 view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx);
7946 view.select_display_ranges(
7947 &[
7948 // an empty selection - the preceding character is deleted
7949 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7950 // one character selected - it is deleted
7951 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7952 // a line suffix selected - it is deleted
7953 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
7954 ],
7955 cx,
7956 );
7957 view.backspace(&Backspace, cx);
7958 assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n");
7959
7960 view.set_text(" one\n two\n three\n four", cx);
7961 view.select_display_ranges(
7962 &[
7963 // cursors at the the end of leading indent - last indent is deleted
7964 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
7965 DisplayPoint::new(1, 8)..DisplayPoint::new(1, 8),
7966 // cursors inside leading indent - overlapping indent deletions are coalesced
7967 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7968 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7969 DisplayPoint::new(2, 6)..DisplayPoint::new(2, 6),
7970 // cursor at the beginning of a line - preceding newline is deleted
7971 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7972 // selection inside leading indent - only the selected character is deleted
7973 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3),
7974 ],
7975 cx,
7976 );
7977 view.backspace(&Backspace, cx);
7978 assert_eq!(view.text(cx), "one\n two\n three four");
7979 });
7980 }
7981
7982 #[gpui::test]
7983 fn test_delete(cx: &mut gpui::MutableAppContext) {
7984 cx.set_global(Settings::test(cx));
7985 let buffer =
7986 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
7987 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7988
7989 view.update(cx, |view, cx| {
7990 view.select_display_ranges(
7991 &[
7992 // an empty selection - the following character is deleted
7993 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7994 // one character selected - it is deleted
7995 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7996 // a line suffix selected - it is deleted
7997 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
7998 ],
7999 cx,
8000 );
8001 view.delete(&Delete, cx);
8002 });
8003
8004 assert_eq!(
8005 buffer.read(cx).read(cx).text(),
8006 "on two three\nfou five six\nseven ten\n"
8007 );
8008 }
8009
8010 #[gpui::test]
8011 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8012 cx.set_global(Settings::test(cx));
8013 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8014 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8015 view.update(cx, |view, cx| {
8016 view.select_display_ranges(
8017 &[
8018 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8019 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8020 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8021 ],
8022 cx,
8023 );
8024 view.delete_line(&DeleteLine, cx);
8025 assert_eq!(view.display_text(cx), "ghi");
8026 assert_eq!(
8027 view.selected_display_ranges(cx),
8028 vec![
8029 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8030 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8031 ]
8032 );
8033 });
8034
8035 cx.set_global(Settings::test(cx));
8036 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8037 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8038 view.update(cx, |view, cx| {
8039 view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx);
8040 view.delete_line(&DeleteLine, cx);
8041 assert_eq!(view.display_text(cx), "ghi\n");
8042 assert_eq!(
8043 view.selected_display_ranges(cx),
8044 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8045 );
8046 });
8047 }
8048
8049 #[gpui::test]
8050 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8051 cx.set_global(Settings::test(cx));
8052 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8053 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8054 view.update(cx, |view, cx| {
8055 view.select_display_ranges(
8056 &[
8057 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8058 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8059 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8060 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8061 ],
8062 cx,
8063 );
8064 view.duplicate_line(&DuplicateLine, cx);
8065 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8066 assert_eq!(
8067 view.selected_display_ranges(cx),
8068 vec![
8069 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8070 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8071 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8072 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8073 ]
8074 );
8075 });
8076
8077 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8078 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8079 view.update(cx, |view, cx| {
8080 view.select_display_ranges(
8081 &[
8082 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8083 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8084 ],
8085 cx,
8086 );
8087 view.duplicate_line(&DuplicateLine, cx);
8088 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8089 assert_eq!(
8090 view.selected_display_ranges(cx),
8091 vec![
8092 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8093 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8094 ]
8095 );
8096 });
8097 }
8098
8099 #[gpui::test]
8100 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8101 cx.set_global(Settings::test(cx));
8102 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8103 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8104 view.update(cx, |view, cx| {
8105 view.fold_ranges(
8106 vec![
8107 Point::new(0, 2)..Point::new(1, 2),
8108 Point::new(2, 3)..Point::new(4, 1),
8109 Point::new(7, 0)..Point::new(8, 4),
8110 ],
8111 cx,
8112 );
8113 view.select_display_ranges(
8114 &[
8115 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8116 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8117 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8118 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8119 ],
8120 cx,
8121 );
8122 assert_eq!(
8123 view.display_text(cx),
8124 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8125 );
8126
8127 view.move_line_up(&MoveLineUp, cx);
8128 assert_eq!(
8129 view.display_text(cx),
8130 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8131 );
8132 assert_eq!(
8133 view.selected_display_ranges(cx),
8134 vec![
8135 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8136 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8137 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8138 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8139 ]
8140 );
8141 });
8142
8143 view.update(cx, |view, cx| {
8144 view.move_line_down(&MoveLineDown, cx);
8145 assert_eq!(
8146 view.display_text(cx),
8147 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8148 );
8149 assert_eq!(
8150 view.selected_display_ranges(cx),
8151 vec![
8152 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8153 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8154 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8155 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8156 ]
8157 );
8158 });
8159
8160 view.update(cx, |view, cx| {
8161 view.move_line_down(&MoveLineDown, cx);
8162 assert_eq!(
8163 view.display_text(cx),
8164 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8165 );
8166 assert_eq!(
8167 view.selected_display_ranges(cx),
8168 vec![
8169 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8170 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8171 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8172 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8173 ]
8174 );
8175 });
8176
8177 view.update(cx, |view, cx| {
8178 view.move_line_up(&MoveLineUp, cx);
8179 assert_eq!(
8180 view.display_text(cx),
8181 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8182 );
8183 assert_eq!(
8184 view.selected_display_ranges(cx),
8185 vec![
8186 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8187 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8188 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8189 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8190 ]
8191 );
8192 });
8193 }
8194
8195 #[gpui::test]
8196 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8197 cx.set_global(Settings::test(cx));
8198 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8199 let snapshot = buffer.read(cx).snapshot(cx);
8200 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8201 editor.update(cx, |editor, cx| {
8202 editor.insert_blocks(
8203 [BlockProperties {
8204 position: snapshot.anchor_after(Point::new(2, 0)),
8205 disposition: BlockDisposition::Below,
8206 height: 1,
8207 render: Arc::new(|_| Empty::new().boxed()),
8208 }],
8209 cx,
8210 );
8211 editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx);
8212 editor.move_line_down(&MoveLineDown, cx);
8213 });
8214 }
8215
8216 #[gpui::test]
8217 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
8218 cx.set_global(Settings::test(cx));
8219 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
8220 let view = cx
8221 .add_window(Default::default(), |cx| build_editor(buffer.clone(), cx))
8222 .1;
8223
8224 // Cut with three selections. Clipboard text is divided into three slices.
8225 view.update(cx, |view, cx| {
8226 view.select_ranges(vec![0..7, 11..17, 22..27], None, cx);
8227 view.cut(&Cut, cx);
8228 assert_eq!(view.display_text(cx), "two four six ");
8229 });
8230
8231 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8232 view.update(cx, |view, cx| {
8233 view.select_ranges(vec![4..4, 9..9, 13..13], None, cx);
8234 view.paste(&Paste, cx);
8235 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
8236 assert_eq!(
8237 view.selected_display_ranges(cx),
8238 &[
8239 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
8240 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
8241 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
8242 ]
8243 );
8244 });
8245
8246 // Paste again but with only two cursors. Since the number of cursors doesn't
8247 // match the number of slices in the clipboard, the entire clipboard text
8248 // is pasted at each cursor.
8249 view.update(cx, |view, cx| {
8250 view.select_ranges(vec![0..0, 31..31], None, cx);
8251 view.handle_input(&Input("( ".into()), cx);
8252 view.paste(&Paste, cx);
8253 view.handle_input(&Input(") ".into()), cx);
8254 assert_eq!(
8255 view.display_text(cx),
8256 "( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8257 );
8258 });
8259
8260 view.update(cx, |view, cx| {
8261 view.select_ranges(vec![0..0], None, cx);
8262 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
8263 assert_eq!(
8264 view.display_text(cx),
8265 "123\n4567\n89\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8266 );
8267 });
8268
8269 // Cut with three selections, one of which is full-line.
8270 view.update(cx, |view, cx| {
8271 view.select_display_ranges(
8272 &[
8273 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
8274 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8275 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
8276 ],
8277 cx,
8278 );
8279 view.cut(&Cut, cx);
8280 assert_eq!(
8281 view.display_text(cx),
8282 "13\n9\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8283 );
8284 });
8285
8286 // Paste with three selections, noticing how the copied selection that was full-line
8287 // gets inserted before the second cursor.
8288 view.update(cx, |view, cx| {
8289 view.select_display_ranges(
8290 &[
8291 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8292 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8293 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
8294 ],
8295 cx,
8296 );
8297 view.paste(&Paste, cx);
8298 assert_eq!(
8299 view.display_text(cx),
8300 "123\n4567\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8301 );
8302 assert_eq!(
8303 view.selected_display_ranges(cx),
8304 &[
8305 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8306 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8307 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
8308 ]
8309 );
8310 });
8311
8312 // Copy with a single cursor only, which writes the whole line into the clipboard.
8313 view.update(cx, |view, cx| {
8314 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx);
8315 view.copy(&Copy, cx);
8316 });
8317
8318 // Paste with three selections, noticing how the copied full-line selection is inserted
8319 // before the empty selections but replaces the selection that is non-empty.
8320 view.update(cx, |view, cx| {
8321 view.select_display_ranges(
8322 &[
8323 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8324 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
8325 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8326 ],
8327 cx,
8328 );
8329 view.paste(&Paste, cx);
8330 assert_eq!(
8331 view.display_text(cx),
8332 "123\n123\n123\n67\n123\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8333 );
8334 assert_eq!(
8335 view.selected_display_ranges(cx),
8336 &[
8337 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8338 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8339 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
8340 ]
8341 );
8342 });
8343 }
8344
8345 #[gpui::test]
8346 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8347 cx.set_global(Settings::test(cx));
8348 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8349 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8350 view.update(cx, |view, cx| {
8351 view.select_all(&SelectAll, cx);
8352 assert_eq!(
8353 view.selected_display_ranges(cx),
8354 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8355 );
8356 });
8357 }
8358
8359 #[gpui::test]
8360 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8361 cx.set_global(Settings::test(cx));
8362 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8363 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8364 view.update(cx, |view, cx| {
8365 view.select_display_ranges(
8366 &[
8367 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8368 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8369 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8370 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8371 ],
8372 cx,
8373 );
8374 view.select_line(&SelectLine, cx);
8375 assert_eq!(
8376 view.selected_display_ranges(cx),
8377 vec![
8378 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8379 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8380 ]
8381 );
8382 });
8383
8384 view.update(cx, |view, cx| {
8385 view.select_line(&SelectLine, cx);
8386 assert_eq!(
8387 view.selected_display_ranges(cx),
8388 vec![
8389 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8390 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8391 ]
8392 );
8393 });
8394
8395 view.update(cx, |view, cx| {
8396 view.select_line(&SelectLine, cx);
8397 assert_eq!(
8398 view.selected_display_ranges(cx),
8399 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8400 );
8401 });
8402 }
8403
8404 #[gpui::test]
8405 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8406 cx.set_global(Settings::test(cx));
8407 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8408 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8409 view.update(cx, |view, cx| {
8410 view.fold_ranges(
8411 vec![
8412 Point::new(0, 2)..Point::new(1, 2),
8413 Point::new(2, 3)..Point::new(4, 1),
8414 Point::new(7, 0)..Point::new(8, 4),
8415 ],
8416 cx,
8417 );
8418 view.select_display_ranges(
8419 &[
8420 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8421 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8422 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8423 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8424 ],
8425 cx,
8426 );
8427 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8428 });
8429
8430 view.update(cx, |view, cx| {
8431 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8432 assert_eq!(
8433 view.display_text(cx),
8434 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8435 );
8436 assert_eq!(
8437 view.selected_display_ranges(cx),
8438 [
8439 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8440 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8441 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8442 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8443 ]
8444 );
8445 });
8446
8447 view.update(cx, |view, cx| {
8448 view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx);
8449 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8450 assert_eq!(
8451 view.display_text(cx),
8452 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8453 );
8454 assert_eq!(
8455 view.selected_display_ranges(cx),
8456 [
8457 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8458 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8459 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8460 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8461 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8462 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8463 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8464 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8465 ]
8466 );
8467 });
8468 }
8469
8470 #[gpui::test]
8471 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8472 cx.set_global(Settings::test(cx));
8473 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8474 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8475
8476 view.update(cx, |view, cx| {
8477 view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx);
8478 });
8479 view.update(cx, |view, cx| {
8480 view.add_selection_above(&AddSelectionAbove, cx);
8481 assert_eq!(
8482 view.selected_display_ranges(cx),
8483 vec![
8484 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8485 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8486 ]
8487 );
8488 });
8489
8490 view.update(cx, |view, cx| {
8491 view.add_selection_above(&AddSelectionAbove, cx);
8492 assert_eq!(
8493 view.selected_display_ranges(cx),
8494 vec![
8495 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8496 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8497 ]
8498 );
8499 });
8500
8501 view.update(cx, |view, cx| {
8502 view.add_selection_below(&AddSelectionBelow, cx);
8503 assert_eq!(
8504 view.selected_display_ranges(cx),
8505 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8506 );
8507
8508 view.undo_selection(&UndoSelection, cx);
8509 assert_eq!(
8510 view.selected_display_ranges(cx),
8511 vec![
8512 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8513 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8514 ]
8515 );
8516
8517 view.redo_selection(&RedoSelection, cx);
8518 assert_eq!(
8519 view.selected_display_ranges(cx),
8520 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8521 );
8522 });
8523
8524 view.update(cx, |view, cx| {
8525 view.add_selection_below(&AddSelectionBelow, cx);
8526 assert_eq!(
8527 view.selected_display_ranges(cx),
8528 vec![
8529 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8530 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8531 ]
8532 );
8533 });
8534
8535 view.update(cx, |view, cx| {
8536 view.add_selection_below(&AddSelectionBelow, cx);
8537 assert_eq!(
8538 view.selected_display_ranges(cx),
8539 vec![
8540 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8541 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8542 ]
8543 );
8544 });
8545
8546 view.update(cx, |view, cx| {
8547 view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx);
8548 });
8549 view.update(cx, |view, cx| {
8550 view.add_selection_below(&AddSelectionBelow, cx);
8551 assert_eq!(
8552 view.selected_display_ranges(cx),
8553 vec![
8554 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8555 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8556 ]
8557 );
8558 });
8559
8560 view.update(cx, |view, cx| {
8561 view.add_selection_below(&AddSelectionBelow, cx);
8562 assert_eq!(
8563 view.selected_display_ranges(cx),
8564 vec![
8565 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8566 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8567 ]
8568 );
8569 });
8570
8571 view.update(cx, |view, cx| {
8572 view.add_selection_above(&AddSelectionAbove, cx);
8573 assert_eq!(
8574 view.selected_display_ranges(cx),
8575 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8576 );
8577 });
8578
8579 view.update(cx, |view, cx| {
8580 view.add_selection_above(&AddSelectionAbove, cx);
8581 assert_eq!(
8582 view.selected_display_ranges(cx),
8583 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8584 );
8585 });
8586
8587 view.update(cx, |view, cx| {
8588 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx);
8589 view.add_selection_below(&AddSelectionBelow, cx);
8590 assert_eq!(
8591 view.selected_display_ranges(cx),
8592 vec![
8593 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8594 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8595 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8596 ]
8597 );
8598 });
8599
8600 view.update(cx, |view, cx| {
8601 view.add_selection_below(&AddSelectionBelow, cx);
8602 assert_eq!(
8603 view.selected_display_ranges(cx),
8604 vec![
8605 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8606 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8607 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8608 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8609 ]
8610 );
8611 });
8612
8613 view.update(cx, |view, cx| {
8614 view.add_selection_above(&AddSelectionAbove, cx);
8615 assert_eq!(
8616 view.selected_display_ranges(cx),
8617 vec![
8618 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8619 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8620 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8621 ]
8622 );
8623 });
8624
8625 view.update(cx, |view, cx| {
8626 view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx);
8627 });
8628 view.update(cx, |view, cx| {
8629 view.add_selection_above(&AddSelectionAbove, cx);
8630 assert_eq!(
8631 view.selected_display_ranges(cx),
8632 vec![
8633 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8634 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8635 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8636 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8637 ]
8638 );
8639 });
8640
8641 view.update(cx, |view, cx| {
8642 view.add_selection_below(&AddSelectionBelow, cx);
8643 assert_eq!(
8644 view.selected_display_ranges(cx),
8645 vec![
8646 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8647 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8648 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8649 ]
8650 );
8651 });
8652 }
8653
8654 #[gpui::test]
8655 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8656 cx.set_global(Settings::test(cx));
8657
8658 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8659 let buffer = MultiBuffer::build_simple(&text, cx);
8660 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8661
8662 view.update(cx, |view, cx| {
8663 view.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None, cx);
8664 view.select_next(
8665 &SelectNext {
8666 replace_newest: false,
8667 },
8668 cx,
8669 );
8670 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8671
8672 view.select_next(
8673 &SelectNext {
8674 replace_newest: false,
8675 },
8676 cx,
8677 );
8678 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8679
8680 view.undo_selection(&UndoSelection, cx);
8681 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8682
8683 view.redo_selection(&RedoSelection, cx);
8684 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8685
8686 view.select_next(
8687 &SelectNext {
8688 replace_newest: false,
8689 },
8690 cx,
8691 );
8692 assert_eq!(view.selected_ranges(cx), &ranges[1..4]);
8693
8694 view.select_next(
8695 &SelectNext {
8696 replace_newest: false,
8697 },
8698 cx,
8699 );
8700 assert_eq!(view.selected_ranges(cx), &ranges[0..4]);
8701 });
8702 }
8703
8704 #[gpui::test]
8705 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8706 cx.update(|cx| cx.set_global(Settings::test(cx)));
8707 let language = Arc::new(Language::new(
8708 LanguageConfig::default(),
8709 Some(tree_sitter_rust::language()),
8710 ));
8711
8712 let text = r#"
8713 use mod1::mod2::{mod3, mod4};
8714
8715 fn fn_1(param1: bool, param2: &str) {
8716 let var1 = "text";
8717 }
8718 "#
8719 .unindent();
8720
8721 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8722 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8723 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8724 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8725 .await;
8726
8727 view.update(cx, |view, cx| {
8728 view.select_display_ranges(
8729 &[
8730 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8731 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8732 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8733 ],
8734 cx,
8735 );
8736 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8737 });
8738 assert_eq!(
8739 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8740 &[
8741 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8742 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8743 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8744 ]
8745 );
8746
8747 view.update(cx, |view, cx| {
8748 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8749 });
8750 assert_eq!(
8751 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8752 &[
8753 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8754 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8755 ]
8756 );
8757
8758 view.update(cx, |view, cx| {
8759 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8760 });
8761 assert_eq!(
8762 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8763 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8764 );
8765
8766 // Trying to expand the selected syntax node one more time has no effect.
8767 view.update(cx, |view, cx| {
8768 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8769 });
8770 assert_eq!(
8771 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8772 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8773 );
8774
8775 view.update(cx, |view, cx| {
8776 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8777 });
8778 assert_eq!(
8779 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8780 &[
8781 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8782 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8783 ]
8784 );
8785
8786 view.update(cx, |view, cx| {
8787 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8788 });
8789 assert_eq!(
8790 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8791 &[
8792 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8793 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8794 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8795 ]
8796 );
8797
8798 view.update(cx, |view, cx| {
8799 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8800 });
8801 assert_eq!(
8802 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8803 &[
8804 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8805 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8806 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8807 ]
8808 );
8809
8810 // Trying to shrink the selected syntax node one more time has no effect.
8811 view.update(cx, |view, cx| {
8812 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8813 });
8814 assert_eq!(
8815 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8816 &[
8817 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8818 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8819 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8820 ]
8821 );
8822
8823 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8824 // a fold.
8825 view.update(cx, |view, cx| {
8826 view.fold_ranges(
8827 vec![
8828 Point::new(0, 21)..Point::new(0, 24),
8829 Point::new(3, 20)..Point::new(3, 22),
8830 ],
8831 cx,
8832 );
8833 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8834 });
8835 assert_eq!(
8836 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8837 &[
8838 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8839 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8840 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8841 ]
8842 );
8843 }
8844
8845 #[gpui::test]
8846 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8847 cx.update(|cx| cx.set_global(Settings::test(cx)));
8848 let language = Arc::new(
8849 Language::new(
8850 LanguageConfig {
8851 brackets: vec![
8852 BracketPair {
8853 start: "{".to_string(),
8854 end: "}".to_string(),
8855 close: false,
8856 newline: true,
8857 },
8858 BracketPair {
8859 start: "(".to_string(),
8860 end: ")".to_string(),
8861 close: false,
8862 newline: true,
8863 },
8864 ],
8865 ..Default::default()
8866 },
8867 Some(tree_sitter_rust::language()),
8868 )
8869 .with_indents_query(
8870 r#"
8871 (_ "(" ")" @end) @indent
8872 (_ "{" "}" @end) @indent
8873 "#,
8874 )
8875 .unwrap(),
8876 );
8877
8878 let text = "fn a() {}";
8879
8880 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8881 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8882 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8883 editor
8884 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8885 .await;
8886
8887 editor.update(cx, |editor, cx| {
8888 editor.select_ranges([5..5, 8..8, 9..9], None, cx);
8889 editor.newline(&Newline, cx);
8890 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8891 assert_eq!(
8892 editor.selected_ranges(cx),
8893 &[
8894 Point::new(1, 4)..Point::new(1, 4),
8895 Point::new(3, 4)..Point::new(3, 4),
8896 Point::new(5, 0)..Point::new(5, 0)
8897 ]
8898 );
8899 });
8900 }
8901
8902 #[gpui::test]
8903 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8904 cx.update(|cx| cx.set_global(Settings::test(cx)));
8905 let language = Arc::new(Language::new(
8906 LanguageConfig {
8907 brackets: vec![
8908 BracketPair {
8909 start: "{".to_string(),
8910 end: "}".to_string(),
8911 close: true,
8912 newline: true,
8913 },
8914 BracketPair {
8915 start: "/*".to_string(),
8916 end: " */".to_string(),
8917 close: true,
8918 newline: true,
8919 },
8920 ],
8921 autoclose_before: "})]".to_string(),
8922 ..Default::default()
8923 },
8924 Some(tree_sitter_rust::language()),
8925 ));
8926
8927 let text = r#"
8928 a
8929
8930 /
8931
8932 "#
8933 .unindent();
8934
8935 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8936 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8937 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8938 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8939 .await;
8940
8941 view.update(cx, |view, cx| {
8942 view.select_display_ranges(
8943 &[
8944 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8945 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8946 ],
8947 cx,
8948 );
8949
8950 view.handle_input(&Input("{".to_string()), cx);
8951 view.handle_input(&Input("{".to_string()), cx);
8952 view.handle_input(&Input("{".to_string()), cx);
8953 assert_eq!(
8954 view.text(cx),
8955 "
8956 {{{}}}
8957 {{{}}}
8958 /
8959
8960 "
8961 .unindent()
8962 );
8963
8964 view.move_right(&MoveRight, cx);
8965 view.handle_input(&Input("}".to_string()), cx);
8966 view.handle_input(&Input("}".to_string()), cx);
8967 view.handle_input(&Input("}".to_string()), cx);
8968 assert_eq!(
8969 view.text(cx),
8970 "
8971 {{{}}}}
8972 {{{}}}}
8973 /
8974
8975 "
8976 .unindent()
8977 );
8978
8979 view.undo(&Undo, cx);
8980 view.handle_input(&Input("/".to_string()), cx);
8981 view.handle_input(&Input("*".to_string()), cx);
8982 assert_eq!(
8983 view.text(cx),
8984 "
8985 /* */
8986 /* */
8987 /
8988
8989 "
8990 .unindent()
8991 );
8992
8993 view.undo(&Undo, cx);
8994 view.select_display_ranges(
8995 &[
8996 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8997 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8998 ],
8999 cx,
9000 );
9001 view.handle_input(&Input("*".to_string()), cx);
9002 assert_eq!(
9003 view.text(cx),
9004 "
9005 a
9006
9007 /*
9008 *
9009 "
9010 .unindent()
9011 );
9012
9013 // Don't autoclose if the next character isn't whitespace and isn't
9014 // listed in the language's "autoclose_before" section.
9015 view.finalize_last_transaction(cx);
9016 view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx);
9017 view.handle_input(&Input("{".to_string()), cx);
9018 assert_eq!(
9019 view.text(cx),
9020 "
9021 {a
9022
9023 /*
9024 *
9025 "
9026 .unindent()
9027 );
9028
9029 view.undo(&Undo, cx);
9030 view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)], cx);
9031 view.handle_input(&Input("{".to_string()), cx);
9032 assert_eq!(
9033 view.text(cx),
9034 "
9035 {a}
9036
9037 /*
9038 *
9039 "
9040 .unindent()
9041 );
9042 assert_eq!(
9043 view.selected_display_ranges(cx),
9044 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9045 );
9046 });
9047 }
9048
9049 #[gpui::test]
9050 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9051 cx.update(|cx| cx.set_global(Settings::test(cx)));
9052
9053 let text = "
9054 a. b
9055 a. b
9056 a. b
9057 "
9058 .unindent();
9059 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9060 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9061
9062 editor.update(cx, |editor, cx| {
9063 let buffer = &editor.snapshot(cx).buffer_snapshot;
9064 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9065 let insertion_ranges = [
9066 Point::new(0, 2).to_offset(buffer)..Point::new(0, 2).to_offset(buffer),
9067 Point::new(1, 2).to_offset(buffer)..Point::new(1, 2).to_offset(buffer),
9068 Point::new(2, 2).to_offset(buffer)..Point::new(2, 2).to_offset(buffer),
9069 ];
9070
9071 editor
9072 .insert_snippet(&insertion_ranges, snippet, cx)
9073 .unwrap();
9074 assert_eq!(
9075 editor.text(cx),
9076 "
9077 a.f(one, two, three) b
9078 a.f(one, two, three) b
9079 a.f(one, two, three) b
9080 "
9081 .unindent()
9082 );
9083 assert_eq!(
9084 editor.selected_ranges::<Point>(cx),
9085 &[
9086 Point::new(0, 4)..Point::new(0, 7),
9087 Point::new(0, 14)..Point::new(0, 19),
9088 Point::new(1, 4)..Point::new(1, 7),
9089 Point::new(1, 14)..Point::new(1, 19),
9090 Point::new(2, 4)..Point::new(2, 7),
9091 Point::new(2, 14)..Point::new(2, 19),
9092 ]
9093 );
9094
9095 // Can't move earlier than the first tab stop
9096 editor.move_to_prev_snippet_tabstop(cx);
9097 assert_eq!(
9098 editor.selected_ranges::<Point>(cx),
9099 &[
9100 Point::new(0, 4)..Point::new(0, 7),
9101 Point::new(0, 14)..Point::new(0, 19),
9102 Point::new(1, 4)..Point::new(1, 7),
9103 Point::new(1, 14)..Point::new(1, 19),
9104 Point::new(2, 4)..Point::new(2, 7),
9105 Point::new(2, 14)..Point::new(2, 19),
9106 ]
9107 );
9108
9109 assert!(editor.move_to_next_snippet_tabstop(cx));
9110 assert_eq!(
9111 editor.selected_ranges::<Point>(cx),
9112 &[
9113 Point::new(0, 9)..Point::new(0, 12),
9114 Point::new(1, 9)..Point::new(1, 12),
9115 Point::new(2, 9)..Point::new(2, 12)
9116 ]
9117 );
9118
9119 editor.move_to_prev_snippet_tabstop(cx);
9120 assert_eq!(
9121 editor.selected_ranges::<Point>(cx),
9122 &[
9123 Point::new(0, 4)..Point::new(0, 7),
9124 Point::new(0, 14)..Point::new(0, 19),
9125 Point::new(1, 4)..Point::new(1, 7),
9126 Point::new(1, 14)..Point::new(1, 19),
9127 Point::new(2, 4)..Point::new(2, 7),
9128 Point::new(2, 14)..Point::new(2, 19),
9129 ]
9130 );
9131
9132 assert!(editor.move_to_next_snippet_tabstop(cx));
9133 assert!(editor.move_to_next_snippet_tabstop(cx));
9134 assert_eq!(
9135 editor.selected_ranges::<Point>(cx),
9136 &[
9137 Point::new(0, 20)..Point::new(0, 20),
9138 Point::new(1, 20)..Point::new(1, 20),
9139 Point::new(2, 20)..Point::new(2, 20)
9140 ]
9141 );
9142
9143 // As soon as the last tab stop is reached, snippet state is gone
9144 editor.move_to_prev_snippet_tabstop(cx);
9145 assert_eq!(
9146 editor.selected_ranges::<Point>(cx),
9147 &[
9148 Point::new(0, 20)..Point::new(0, 20),
9149 Point::new(1, 20)..Point::new(1, 20),
9150 Point::new(2, 20)..Point::new(2, 20)
9151 ]
9152 );
9153 });
9154 }
9155
9156 #[gpui::test]
9157 async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
9158 cx.foreground().forbid_parking();
9159 cx.update(|cx| cx.set_global(Settings::test(cx)));
9160
9161 let mut language = Language::new(
9162 LanguageConfig {
9163 name: "Rust".into(),
9164 path_suffixes: vec!["rs".to_string()],
9165 ..Default::default()
9166 },
9167 Some(tree_sitter_rust::language()),
9168 );
9169 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9170 capabilities: lsp::ServerCapabilities {
9171 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9172 ..Default::default()
9173 },
9174 ..Default::default()
9175 });
9176
9177 let fs = FakeFs::new(cx.background().clone());
9178 fs.insert_file("/file.rs", Default::default()).await;
9179
9180 let project = Project::test(fs, cx);
9181 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9182
9183 let worktree_id = project
9184 .update(cx, |project, cx| {
9185 project.find_or_create_local_worktree("/file.rs", true, cx)
9186 })
9187 .await
9188 .unwrap()
9189 .0
9190 .read_with(cx, |tree, _| tree.id());
9191 let buffer = project
9192 .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
9193 .await
9194 .unwrap();
9195
9196 cx.foreground().start_waiting();
9197 let fake_server = fake_servers.next().await.unwrap();
9198
9199 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9200 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9201 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9202 assert!(cx.read(|cx| editor.is_dirty(cx)));
9203
9204 let save = cx.update(|cx| editor.save(project.clone(), cx));
9205 fake_server
9206 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9207 assert_eq!(
9208 params.text_document.uri,
9209 lsp::Url::from_file_path("/file.rs").unwrap()
9210 );
9211 assert_eq!(params.options.tab_size, 4);
9212 Ok(Some(vec![lsp::TextEdit::new(
9213 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9214 ", ".to_string(),
9215 )]))
9216 })
9217 .next()
9218 .await;
9219 cx.foreground().start_waiting();
9220 save.await.unwrap();
9221 assert_eq!(
9222 editor.read_with(cx, |editor, cx| editor.text(cx)),
9223 "one, two\nthree\n"
9224 );
9225 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9226
9227 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9228 assert!(cx.read(|cx| editor.is_dirty(cx)));
9229
9230 // Ensure we can still save even if formatting hangs.
9231 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9232 assert_eq!(
9233 params.text_document.uri,
9234 lsp::Url::from_file_path("/file.rs").unwrap()
9235 );
9236 futures::future::pending::<()>().await;
9237 unreachable!()
9238 });
9239 let save = cx.update(|cx| editor.save(project.clone(), cx));
9240 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9241 cx.foreground().start_waiting();
9242 save.await.unwrap();
9243 assert_eq!(
9244 editor.read_with(cx, |editor, cx| editor.text(cx)),
9245 "one\ntwo\nthree\n"
9246 );
9247 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9248
9249 // Set rust language override and assert overriden tabsize is sent to language server
9250 cx.update(|cx| {
9251 cx.update_global::<Settings, _, _>(|settings, _| {
9252 settings.language_overrides.insert(
9253 "Rust".into(),
9254 LanguageOverride {
9255 tab_size: Some(8),
9256 ..Default::default()
9257 },
9258 );
9259 })
9260 });
9261
9262 let save = cx.update(|cx| editor.save(project.clone(), cx));
9263 fake_server
9264 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9265 assert_eq!(
9266 params.text_document.uri,
9267 lsp::Url::from_file_path("/file.rs").unwrap()
9268 );
9269 assert_eq!(params.options.tab_size, 8);
9270 Ok(Some(vec![]))
9271 })
9272 .next()
9273 .await;
9274 cx.foreground().start_waiting();
9275 save.await.unwrap();
9276 }
9277
9278 #[gpui::test]
9279 async fn test_completion(cx: &mut gpui::TestAppContext) {
9280 cx.update(|cx| cx.set_global(Settings::test(cx)));
9281
9282 let mut language = Language::new(
9283 LanguageConfig {
9284 name: "Rust".into(),
9285 path_suffixes: vec!["rs".to_string()],
9286 ..Default::default()
9287 },
9288 Some(tree_sitter_rust::language()),
9289 );
9290 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9291 capabilities: lsp::ServerCapabilities {
9292 completion_provider: Some(lsp::CompletionOptions {
9293 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9294 ..Default::default()
9295 }),
9296 ..Default::default()
9297 },
9298 ..Default::default()
9299 });
9300
9301 let text = "
9302 one
9303 two
9304 three
9305 "
9306 .unindent();
9307
9308 let fs = FakeFs::new(cx.background().clone());
9309 fs.insert_file("/file.rs", text).await;
9310
9311 let project = Project::test(fs, cx);
9312 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9313
9314 let worktree_id = project
9315 .update(cx, |project, cx| {
9316 project.find_or_create_local_worktree("/file.rs", true, cx)
9317 })
9318 .await
9319 .unwrap()
9320 .0
9321 .read_with(cx, |tree, _| tree.id());
9322 let buffer = project
9323 .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
9324 .await
9325 .unwrap();
9326 let mut fake_server = fake_servers.next().await.unwrap();
9327
9328 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9329 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9330
9331 editor.update(cx, |editor, cx| {
9332 editor.project = Some(project);
9333 editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx);
9334 editor.handle_input(&Input(".".to_string()), cx);
9335 });
9336
9337 handle_completion_request(
9338 &mut fake_server,
9339 "/file.rs",
9340 Point::new(0, 4),
9341 vec![
9342 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9343 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9344 ],
9345 )
9346 .await;
9347 editor
9348 .condition(&cx, |editor, _| editor.context_menu_visible())
9349 .await;
9350
9351 let apply_additional_edits = editor.update(cx, |editor, cx| {
9352 editor.move_down(&MoveDown, cx);
9353 let apply_additional_edits = editor
9354 .confirm_completion(&ConfirmCompletion::default(), cx)
9355 .unwrap();
9356 assert_eq!(
9357 editor.text(cx),
9358 "
9359 one.second_completion
9360 two
9361 three
9362 "
9363 .unindent()
9364 );
9365 apply_additional_edits
9366 });
9367
9368 handle_resolve_completion_request(
9369 &mut fake_server,
9370 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9371 )
9372 .await;
9373 apply_additional_edits.await.unwrap();
9374 assert_eq!(
9375 editor.read_with(cx, |editor, cx| editor.text(cx)),
9376 "
9377 one.second_completion
9378 two
9379 three
9380 additional edit
9381 "
9382 .unindent()
9383 );
9384
9385 editor.update(cx, |editor, cx| {
9386 editor.select_ranges(
9387 [
9388 Point::new(1, 3)..Point::new(1, 3),
9389 Point::new(2, 5)..Point::new(2, 5),
9390 ],
9391 None,
9392 cx,
9393 );
9394
9395 editor.handle_input(&Input(" ".to_string()), cx);
9396 assert!(editor.context_menu.is_none());
9397 editor.handle_input(&Input("s".to_string()), cx);
9398 assert!(editor.context_menu.is_none());
9399 });
9400
9401 handle_completion_request(
9402 &mut fake_server,
9403 "/file.rs",
9404 Point::new(2, 7),
9405 vec![
9406 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9407 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9408 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9409 ],
9410 )
9411 .await;
9412 editor
9413 .condition(&cx, |editor, _| editor.context_menu_visible())
9414 .await;
9415
9416 editor.update(cx, |editor, cx| {
9417 editor.handle_input(&Input("i".to_string()), cx);
9418 });
9419
9420 handle_completion_request(
9421 &mut fake_server,
9422 "/file.rs",
9423 Point::new(2, 8),
9424 vec![
9425 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9426 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9427 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9428 ],
9429 )
9430 .await;
9431 editor
9432 .condition(&cx, |editor, _| editor.context_menu_visible())
9433 .await;
9434
9435 let apply_additional_edits = editor.update(cx, |editor, cx| {
9436 let apply_additional_edits = editor
9437 .confirm_completion(&ConfirmCompletion::default(), cx)
9438 .unwrap();
9439 assert_eq!(
9440 editor.text(cx),
9441 "
9442 one.second_completion
9443 two sixth_completion
9444 three sixth_completion
9445 additional edit
9446 "
9447 .unindent()
9448 );
9449 apply_additional_edits
9450 });
9451 handle_resolve_completion_request(&mut fake_server, None).await;
9452 apply_additional_edits.await.unwrap();
9453
9454 async fn handle_completion_request(
9455 fake: &mut FakeLanguageServer,
9456 path: &'static str,
9457 position: Point,
9458 completions: Vec<(Range<Point>, &'static str)>,
9459 ) {
9460 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9461 let completions = completions.clone();
9462 async move {
9463 assert_eq!(
9464 params.text_document_position.text_document.uri,
9465 lsp::Url::from_file_path(path).unwrap()
9466 );
9467 assert_eq!(
9468 params.text_document_position.position,
9469 lsp::Position::new(position.row, position.column)
9470 );
9471 Ok(Some(lsp::CompletionResponse::Array(
9472 completions
9473 .iter()
9474 .map(|(range, new_text)| lsp::CompletionItem {
9475 label: new_text.to_string(),
9476 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9477 range: lsp::Range::new(
9478 lsp::Position::new(range.start.row, range.start.column),
9479 lsp::Position::new(range.start.row, range.start.column),
9480 ),
9481 new_text: new_text.to_string(),
9482 })),
9483 ..Default::default()
9484 })
9485 .collect(),
9486 )))
9487 }
9488 })
9489 .next()
9490 .await;
9491 }
9492
9493 async fn handle_resolve_completion_request(
9494 fake: &mut FakeLanguageServer,
9495 edit: Option<(Range<Point>, &'static str)>,
9496 ) {
9497 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9498 let edit = edit.clone();
9499 async move {
9500 Ok(lsp::CompletionItem {
9501 additional_text_edits: edit.map(|(range, new_text)| {
9502 vec![lsp::TextEdit::new(
9503 lsp::Range::new(
9504 lsp::Position::new(range.start.row, range.start.column),
9505 lsp::Position::new(range.end.row, range.end.column),
9506 ),
9507 new_text.to_string(),
9508 )]
9509 }),
9510 ..Default::default()
9511 })
9512 }
9513 })
9514 .next()
9515 .await;
9516 }
9517 }
9518
9519 #[gpui::test]
9520 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9521 cx.update(|cx| cx.set_global(Settings::test(cx)));
9522 let language = Arc::new(Language::new(
9523 LanguageConfig {
9524 line_comment: Some("// ".to_string()),
9525 ..Default::default()
9526 },
9527 Some(tree_sitter_rust::language()),
9528 ));
9529
9530 let text = "
9531 fn a() {
9532 //b();
9533 // c();
9534 // d();
9535 }
9536 "
9537 .unindent();
9538
9539 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9540 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9541 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9542
9543 view.update(cx, |editor, cx| {
9544 // If multiple selections intersect a line, the line is only
9545 // toggled once.
9546 editor.select_display_ranges(
9547 &[
9548 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9549 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9550 ],
9551 cx,
9552 );
9553 editor.toggle_comments(&ToggleComments, cx);
9554 assert_eq!(
9555 editor.text(cx),
9556 "
9557 fn a() {
9558 b();
9559 c();
9560 d();
9561 }
9562 "
9563 .unindent()
9564 );
9565
9566 // The comment prefix is inserted at the same column for every line
9567 // in a selection.
9568 editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx);
9569 editor.toggle_comments(&ToggleComments, cx);
9570 assert_eq!(
9571 editor.text(cx),
9572 "
9573 fn a() {
9574 // b();
9575 // c();
9576 // d();
9577 }
9578 "
9579 .unindent()
9580 );
9581
9582 // If a selection ends at the beginning of a line, that line is not toggled.
9583 editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx);
9584 editor.toggle_comments(&ToggleComments, cx);
9585 assert_eq!(
9586 editor.text(cx),
9587 "
9588 fn a() {
9589 // b();
9590 c();
9591 // d();
9592 }
9593 "
9594 .unindent()
9595 );
9596 });
9597 }
9598
9599 #[gpui::test]
9600 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9601 cx.set_global(Settings::test(cx));
9602 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9603 let multibuffer = cx.add_model(|cx| {
9604 let mut multibuffer = MultiBuffer::new(0);
9605 multibuffer.push_excerpts(
9606 buffer.clone(),
9607 [
9608 Point::new(0, 0)..Point::new(0, 4),
9609 Point::new(1, 0)..Point::new(1, 4),
9610 ],
9611 cx,
9612 );
9613 multibuffer
9614 });
9615
9616 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9617
9618 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9619 view.update(cx, |view, cx| {
9620 assert_eq!(view.text(cx), "aaaa\nbbbb");
9621 view.select_ranges(
9622 [
9623 Point::new(0, 0)..Point::new(0, 0),
9624 Point::new(1, 0)..Point::new(1, 0),
9625 ],
9626 None,
9627 cx,
9628 );
9629
9630 view.handle_input(&Input("X".to_string()), cx);
9631 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9632 assert_eq!(
9633 view.selected_ranges(cx),
9634 [
9635 Point::new(0, 1)..Point::new(0, 1),
9636 Point::new(1, 1)..Point::new(1, 1),
9637 ]
9638 )
9639 });
9640 }
9641
9642 #[gpui::test]
9643 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9644 cx.set_global(Settings::test(cx));
9645 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9646 let multibuffer = cx.add_model(|cx| {
9647 let mut multibuffer = MultiBuffer::new(0);
9648 multibuffer.push_excerpts(
9649 buffer,
9650 [
9651 Point::new(0, 0)..Point::new(1, 4),
9652 Point::new(1, 0)..Point::new(2, 4),
9653 ],
9654 cx,
9655 );
9656 multibuffer
9657 });
9658
9659 assert_eq!(
9660 multibuffer.read(cx).read(cx).text(),
9661 "aaaa\nbbbb\nbbbb\ncccc"
9662 );
9663
9664 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9665 view.update(cx, |view, cx| {
9666 view.select_ranges(
9667 [
9668 Point::new(1, 1)..Point::new(1, 1),
9669 Point::new(2, 3)..Point::new(2, 3),
9670 ],
9671 None,
9672 cx,
9673 );
9674
9675 view.handle_input(&Input("X".to_string()), cx);
9676 assert_eq!(view.text(cx), "aaaa\nbXbbXb\nbXbbXb\ncccc");
9677 assert_eq!(
9678 view.selected_ranges(cx),
9679 [
9680 Point::new(1, 2)..Point::new(1, 2),
9681 Point::new(2, 5)..Point::new(2, 5),
9682 ]
9683 );
9684
9685 view.newline(&Newline, cx);
9686 assert_eq!(view.text(cx), "aaaa\nbX\nbbX\nb\nbX\nbbX\nb\ncccc");
9687 assert_eq!(
9688 view.selected_ranges(cx),
9689 [
9690 Point::new(2, 0)..Point::new(2, 0),
9691 Point::new(6, 0)..Point::new(6, 0),
9692 ]
9693 );
9694 });
9695 }
9696
9697 #[gpui::test]
9698 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9699 cx.set_global(Settings::test(cx));
9700 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9701 let mut excerpt1_id = None;
9702 let multibuffer = cx.add_model(|cx| {
9703 let mut multibuffer = MultiBuffer::new(0);
9704 excerpt1_id = multibuffer
9705 .push_excerpts(
9706 buffer.clone(),
9707 [
9708 Point::new(0, 0)..Point::new(1, 4),
9709 Point::new(1, 0)..Point::new(2, 4),
9710 ],
9711 cx,
9712 )
9713 .into_iter()
9714 .next();
9715 multibuffer
9716 });
9717 assert_eq!(
9718 multibuffer.read(cx).read(cx).text(),
9719 "aaaa\nbbbb\nbbbb\ncccc"
9720 );
9721 let (_, editor) = cx.add_window(Default::default(), |cx| {
9722 let mut editor = build_editor(multibuffer.clone(), cx);
9723 let snapshot = editor.snapshot(cx);
9724 editor.select_ranges([Point::new(1, 3)..Point::new(1, 3)], None, cx);
9725 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9726 assert_eq!(
9727 editor.selected_ranges(cx),
9728 [
9729 Point::new(1, 3)..Point::new(1, 3),
9730 Point::new(2, 1)..Point::new(2, 1),
9731 ]
9732 );
9733 editor
9734 });
9735
9736 // Refreshing selections is a no-op when excerpts haven't changed.
9737 editor.update(cx, |editor, cx| {
9738 editor.refresh_selections(cx);
9739 assert_eq!(
9740 editor.selected_ranges(cx),
9741 [
9742 Point::new(1, 3)..Point::new(1, 3),
9743 Point::new(2, 1)..Point::new(2, 1),
9744 ]
9745 );
9746 });
9747
9748 multibuffer.update(cx, |multibuffer, cx| {
9749 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9750 });
9751 editor.update(cx, |editor, cx| {
9752 // Removing an excerpt causes the first selection to become degenerate.
9753 assert_eq!(
9754 editor.selected_ranges(cx),
9755 [
9756 Point::new(0, 0)..Point::new(0, 0),
9757 Point::new(0, 1)..Point::new(0, 1)
9758 ]
9759 );
9760
9761 // Refreshing selections will relocate the first selection to the original buffer
9762 // location.
9763 editor.refresh_selections(cx);
9764 assert_eq!(
9765 editor.selected_ranges(cx),
9766 [
9767 Point::new(0, 1)..Point::new(0, 1),
9768 Point::new(0, 3)..Point::new(0, 3)
9769 ]
9770 );
9771 assert!(editor.pending_selection.is_some());
9772 });
9773 }
9774
9775 #[gpui::test]
9776 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9777 cx.set_global(Settings::test(cx));
9778 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9779 let mut excerpt1_id = None;
9780 let multibuffer = cx.add_model(|cx| {
9781 let mut multibuffer = MultiBuffer::new(0);
9782 excerpt1_id = multibuffer
9783 .push_excerpts(
9784 buffer.clone(),
9785 [
9786 Point::new(0, 0)..Point::new(1, 4),
9787 Point::new(1, 0)..Point::new(2, 4),
9788 ],
9789 cx,
9790 )
9791 .into_iter()
9792 .next();
9793 multibuffer
9794 });
9795 assert_eq!(
9796 multibuffer.read(cx).read(cx).text(),
9797 "aaaa\nbbbb\nbbbb\ncccc"
9798 );
9799 let (_, editor) = cx.add_window(Default::default(), |cx| {
9800 let mut editor = build_editor(multibuffer.clone(), cx);
9801 let snapshot = editor.snapshot(cx);
9802 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9803 assert_eq!(
9804 editor.selected_ranges(cx),
9805 [Point::new(1, 3)..Point::new(1, 3)]
9806 );
9807 editor
9808 });
9809
9810 multibuffer.update(cx, |multibuffer, cx| {
9811 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9812 });
9813 editor.update(cx, |editor, cx| {
9814 assert_eq!(
9815 editor.selected_ranges(cx),
9816 [Point::new(0, 0)..Point::new(0, 0)]
9817 );
9818
9819 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9820 editor.refresh_selections(cx);
9821 assert_eq!(
9822 editor.selected_ranges(cx),
9823 [Point::new(0, 3)..Point::new(0, 3)]
9824 );
9825 assert!(editor.pending_selection.is_some());
9826 });
9827 }
9828
9829 #[gpui::test]
9830 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9831 cx.update(|cx| cx.set_global(Settings::test(cx)));
9832 let language = Arc::new(Language::new(
9833 LanguageConfig {
9834 brackets: vec![
9835 BracketPair {
9836 start: "{".to_string(),
9837 end: "}".to_string(),
9838 close: true,
9839 newline: true,
9840 },
9841 BracketPair {
9842 start: "/* ".to_string(),
9843 end: " */".to_string(),
9844 close: true,
9845 newline: true,
9846 },
9847 ],
9848 ..Default::default()
9849 },
9850 Some(tree_sitter_rust::language()),
9851 ));
9852
9853 let text = concat!(
9854 "{ }\n", // Suppress rustfmt
9855 " x\n", //
9856 " /* */\n", //
9857 "x\n", //
9858 "{{} }\n", //
9859 );
9860
9861 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9862 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9863 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9864 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9865 .await;
9866
9867 view.update(cx, |view, cx| {
9868 view.select_display_ranges(
9869 &[
9870 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9871 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9872 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9873 ],
9874 cx,
9875 );
9876 view.newline(&Newline, cx);
9877
9878 assert_eq!(
9879 view.buffer().read(cx).read(cx).text(),
9880 concat!(
9881 "{ \n", // Suppress rustfmt
9882 "\n", //
9883 "}\n", //
9884 " x\n", //
9885 " /* \n", //
9886 " \n", //
9887 " */\n", //
9888 "x\n", //
9889 "{{} \n", //
9890 "}\n", //
9891 )
9892 );
9893 });
9894 }
9895
9896 #[gpui::test]
9897 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9898 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9899
9900 cx.set_global(Settings::test(cx));
9901 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9902
9903 editor.update(cx, |editor, cx| {
9904 struct Type1;
9905 struct Type2;
9906
9907 let buffer = buffer.read(cx).snapshot(cx);
9908
9909 let anchor_range = |range: Range<Point>| {
9910 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9911 };
9912
9913 editor.highlight_background::<Type1>(
9914 vec![
9915 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
9916 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
9917 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
9918 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
9919 ],
9920 |_| Color::red(),
9921 cx,
9922 );
9923 editor.highlight_background::<Type2>(
9924 vec![
9925 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
9926 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
9927 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
9928 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
9929 ],
9930 |_| Color::green(),
9931 cx,
9932 );
9933
9934 let snapshot = editor.snapshot(cx);
9935 let mut highlighted_ranges = editor.background_highlights_in_range(
9936 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
9937 &snapshot,
9938 cx.global::<Settings>().theme.as_ref(),
9939 );
9940 // Enforce a consistent ordering based on color without relying on the ordering of the
9941 // highlight's `TypeId` which is non-deterministic.
9942 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
9943 assert_eq!(
9944 highlighted_ranges,
9945 &[
9946 (
9947 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
9948 Color::green(),
9949 ),
9950 (
9951 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
9952 Color::green(),
9953 ),
9954 (
9955 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
9956 Color::red(),
9957 ),
9958 (
9959 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9960 Color::red(),
9961 ),
9962 ]
9963 );
9964 assert_eq!(
9965 editor.background_highlights_in_range(
9966 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
9967 &snapshot,
9968 cx.global::<Settings>().theme.as_ref(),
9969 ),
9970 &[(
9971 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9972 Color::red(),
9973 )]
9974 );
9975 });
9976 }
9977
9978 #[gpui::test]
9979 fn test_following(cx: &mut gpui::MutableAppContext) {
9980 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9981
9982 cx.set_global(Settings::test(cx));
9983
9984 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9985 let (_, follower) = cx.add_window(
9986 WindowOptions {
9987 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
9988 ..Default::default()
9989 },
9990 |cx| build_editor(buffer.clone(), cx),
9991 );
9992
9993 let pending_update = Rc::new(RefCell::new(None));
9994 follower.update(cx, {
9995 let update = pending_update.clone();
9996 |_, cx| {
9997 cx.subscribe(&leader, move |_, leader, event, cx| {
9998 leader
9999 .read(cx)
10000 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10001 })
10002 .detach();
10003 }
10004 });
10005
10006 // Update the selections only
10007 leader.update(cx, |leader, cx| {
10008 leader.select_ranges([1..1], None, cx);
10009 });
10010 follower.update(cx, |follower, cx| {
10011 follower
10012 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10013 .unwrap();
10014 });
10015 assert_eq!(follower.read(cx).selected_ranges(cx), vec![1..1]);
10016
10017 // Update the scroll position only
10018 leader.update(cx, |leader, cx| {
10019 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10020 });
10021 follower.update(cx, |follower, cx| {
10022 follower
10023 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10024 .unwrap();
10025 });
10026 assert_eq!(
10027 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10028 vec2f(1.5, 3.5)
10029 );
10030
10031 // Update the selections and scroll position
10032 leader.update(cx, |leader, cx| {
10033 leader.select_ranges([0..0], None, cx);
10034 leader.request_autoscroll(Autoscroll::Newest, cx);
10035 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10036 });
10037 follower.update(cx, |follower, cx| {
10038 let initial_scroll_position = follower.scroll_position(cx);
10039 follower
10040 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10041 .unwrap();
10042 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10043 assert!(follower.autoscroll_request.is_some());
10044 });
10045 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0]);
10046
10047 // Creating a pending selection that precedes another selection
10048 leader.update(cx, |leader, cx| {
10049 leader.select_ranges([1..1], None, cx);
10050 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10051 });
10052 follower.update(cx, |follower, cx| {
10053 follower
10054 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10055 .unwrap();
10056 });
10057 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0, 1..1]);
10058
10059 // Extend the pending selection so that it surrounds another selection
10060 leader.update(cx, |leader, cx| {
10061 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10062 });
10063 follower.update(cx, |follower, cx| {
10064 follower
10065 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10066 .unwrap();
10067 });
10068 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..2]);
10069 }
10070
10071 #[test]
10072 fn test_combine_syntax_and_fuzzy_match_highlights() {
10073 let string = "abcdefghijklmnop";
10074 let syntax_ranges = [
10075 (
10076 0..3,
10077 HighlightStyle {
10078 color: Some(Color::red()),
10079 ..Default::default()
10080 },
10081 ),
10082 (
10083 4..8,
10084 HighlightStyle {
10085 color: Some(Color::green()),
10086 ..Default::default()
10087 },
10088 ),
10089 ];
10090 let match_indices = [4, 6, 7, 8];
10091 assert_eq!(
10092 combine_syntax_and_fuzzy_match_highlights(
10093 &string,
10094 Default::default(),
10095 syntax_ranges.into_iter(),
10096 &match_indices,
10097 ),
10098 &[
10099 (
10100 0..3,
10101 HighlightStyle {
10102 color: Some(Color::red()),
10103 ..Default::default()
10104 },
10105 ),
10106 (
10107 4..5,
10108 HighlightStyle {
10109 color: Some(Color::green()),
10110 weight: Some(fonts::Weight::BOLD),
10111 ..Default::default()
10112 },
10113 ),
10114 (
10115 5..6,
10116 HighlightStyle {
10117 color: Some(Color::green()),
10118 ..Default::default()
10119 },
10120 ),
10121 (
10122 6..8,
10123 HighlightStyle {
10124 color: Some(Color::green()),
10125 weight: Some(fonts::Weight::BOLD),
10126 ..Default::default()
10127 },
10128 ),
10129 (
10130 8..9,
10131 HighlightStyle {
10132 weight: Some(fonts::Weight::BOLD),
10133 ..Default::default()
10134 },
10135 ),
10136 ]
10137 );
10138 }
10139
10140 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10141 let point = DisplayPoint::new(row as u32, column as u32);
10142 point..point
10143 }
10144
10145 fn build_editor(buffer: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
10146 Editor::new(EditorMode::Full, buffer, None, None, cx)
10147 }
10148
10149 fn assert_selection_ranges(
10150 marked_text: &str,
10151 selection_marker_pairs: Vec<(char, char)>,
10152 view: &mut Editor,
10153 cx: &mut ViewContext<Editor>,
10154 ) {
10155 let snapshot = view.snapshot(cx).display_snapshot;
10156 let mut marker_chars = Vec::new();
10157 for (start, end) in selection_marker_pairs.iter() {
10158 marker_chars.push(*start);
10159 marker_chars.push(*end);
10160 }
10161 let (_, markers) = marked_text_by(marked_text, marker_chars);
10162 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10163 .iter()
10164 .map(|(start, end)| {
10165 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10166 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10167 start..end
10168 })
10169 .collect();
10170 assert_eq!(
10171 view.selected_display_ranges(cx),
10172 &asserted_ranges[..],
10173 "Assert selections are {}",
10174 marked_text
10175 );
10176 }
10177}
10178
10179trait RangeExt<T> {
10180 fn sorted(&self) -> Range<T>;
10181 fn to_inclusive(&self) -> RangeInclusive<T>;
10182}
10183
10184impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10185 fn sorted(&self) -> Self {
10186 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10187 }
10188
10189 fn to_inclusive(&self) -> RangeInclusive<T> {
10190 self.start.clone()..=self.end.clone()
10191 }
10192}