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