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