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