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