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