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