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