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