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