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