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