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