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