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