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