1pub mod display_map;
2mod element;
3pub mod items;
4pub mod movement;
5mod multi_buffer;
6
7#[cfg(test)]
8mod test;
9
10use aho_corasick::AhoCorasick;
11use anyhow::Result;
12use clock::ReplicaId;
13use collections::{BTreeMap, Bound, HashMap, HashSet};
14pub use display_map::DisplayPoint;
15use display_map::*;
16pub use element::*;
17use fuzzy::{StringMatch, StringMatchCandidate};
18use gpui::{
19 action,
20 color::Color,
21 elements::*,
22 executor,
23 fonts::{self, HighlightStyle, TextStyle},
24 geometry::vector::{vec2f, Vector2F},
25 keymap::Binding,
26 platform::CursorStyle,
27 text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
28 ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
29 WeakModelHandle, WeakViewHandle,
30};
31use items::{BufferItemHandle, MultiBufferItemHandle};
32use itertools::Itertools as _;
33use language::{
34 AnchorRangeExt as _, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
35 DiagnosticSeverity, Language, Point, Selection, SelectionGoal, TransactionId,
36};
37use multi_buffer::MultiBufferChunks;
38pub use multi_buffer::{
39 char_kind, Anchor, AnchorRangeExt, CharKind, ExcerptId, MultiBuffer, MultiBufferSnapshot,
40 ToOffset, ToPoint,
41};
42use ordered_float::OrderedFloat;
43use postage::watch;
44use project::{Project, ProjectTransaction};
45use serde::{Deserialize, Serialize};
46use smallvec::SmallVec;
47use smol::Timer;
48use snippet::Snippet;
49use std::{
50 any::TypeId,
51 cmp::{self, Ordering, Reverse},
52 iter::{self, FromIterator},
53 mem,
54 ops::{Deref, DerefMut, Range, RangeInclusive, Sub},
55 sync::Arc,
56 time::{Duration, Instant},
57};
58pub use sum_tree::Bias;
59use text::rope::TextDimension;
60use theme::{DiagnosticStyle, EditorStyle};
61use util::{post_inc, ResultExt, TryFutureExt};
62use workspace::{ItemNavHistory, PathOpener, Workspace};
63
64const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
65const MAX_LINE_LEN: usize = 1024;
66const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
67
68action!(Cancel);
69action!(Backspace);
70action!(Delete);
71action!(Input, String);
72action!(Newline);
73action!(Tab);
74action!(Outdent);
75action!(DeleteLine);
76action!(DeleteToPreviousWordBoundary);
77action!(DeleteToNextWordBoundary);
78action!(DeleteToBeginningOfLine);
79action!(DeleteToEndOfLine);
80action!(CutToEndOfLine);
81action!(DuplicateLine);
82action!(MoveLineUp);
83action!(MoveLineDown);
84action!(Cut);
85action!(Copy);
86action!(Paste);
87action!(Undo);
88action!(Redo);
89action!(MoveUp);
90action!(MoveDown);
91action!(MoveLeft);
92action!(MoveRight);
93action!(MoveToPreviousWordBoundary);
94action!(MoveToNextWordBoundary);
95action!(MoveToBeginningOfLine);
96action!(MoveToEndOfLine);
97action!(MoveToBeginning);
98action!(MoveToEnd);
99action!(SelectUp);
100action!(SelectDown);
101action!(SelectLeft);
102action!(SelectRight);
103action!(SelectToPreviousWordBoundary);
104action!(SelectToNextWordBoundary);
105action!(SelectToBeginningOfLine, bool);
106action!(SelectToEndOfLine, bool);
107action!(SelectToBeginning);
108action!(SelectToEnd);
109action!(SelectAll);
110action!(SelectLine);
111action!(SplitSelectionIntoLines);
112action!(AddSelectionAbove);
113action!(AddSelectionBelow);
114action!(SelectNext, bool);
115action!(ToggleComments);
116action!(SelectLargerSyntaxNode);
117action!(SelectSmallerSyntaxNode);
118action!(MoveToEnclosingBracket);
119action!(ShowNextDiagnostic);
120action!(GoToDefinition);
121action!(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_code_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 this.take_rename(cx);
4135 let settings = (this.build_settings)(cx);
4136 let buffer = this.buffer.read(cx).read(cx);
4137 let cursor_offset = selection.head().to_offset(&buffer);
4138 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4139 let rename_end = rename_start + rename_buffer_range.len();
4140 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4141 let old_name = buffer
4142 .text_for_range(rename_start..rename_end)
4143 .collect::<String>();
4144 drop(buffer);
4145
4146 // Position the selection in the rename editor so that it matches the current selection.
4147 let rename_editor = cx.add_view(|cx| {
4148 let mut editor = Editor::single_line(this.build_settings.clone(), cx);
4149 editor
4150 .buffer
4151 .update(cx, |buffer, cx| buffer.edit([0..0], &old_name, cx));
4152 editor.select_ranges(
4153 [tail_offset_in_rename_range..cursor_offset_in_rename_range],
4154 None,
4155 cx,
4156 );
4157 editor.highlight_ranges::<Rename>(
4158 vec![Anchor::min()..Anchor::max()],
4159 settings.style.diff_background_inserted,
4160 cx,
4161 );
4162 editor
4163 });
4164 this.highlight_ranges::<Rename>(
4165 vec![range.clone()],
4166 settings.style.diff_background_deleted,
4167 cx,
4168 );
4169 this.update_selections(
4170 vec![Selection {
4171 id: selection.id,
4172 start: rename_end,
4173 end: rename_end,
4174 reversed: false,
4175 goal: SelectionGoal::None,
4176 }],
4177 None,
4178 cx,
4179 );
4180 cx.focus(&rename_editor);
4181 let block_id = this.insert_blocks(
4182 [BlockProperties {
4183 position: range.start.clone(),
4184 height: 1,
4185 render: Arc::new({
4186 let editor = rename_editor.clone();
4187 move |cx: &BlockContext| {
4188 ChildView::new(editor.clone())
4189 .contained()
4190 .with_padding_left(cx.anchor_x)
4191 .boxed()
4192 }
4193 }),
4194 disposition: BlockDisposition::Below,
4195 }],
4196 cx,
4197 )[0];
4198 this.pending_rename = Some(RenameState {
4199 range,
4200 old_name,
4201 editor: rename_editor,
4202 block_id,
4203 });
4204 });
4205 }
4206
4207 Ok(())
4208 }))
4209 }
4210
4211 pub fn confirm_rename(
4212 workspace: &mut Workspace,
4213 _: &ConfirmRename,
4214 cx: &mut ViewContext<Workspace>,
4215 ) -> Option<Task<Result<()>>> {
4216 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4217
4218 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4219 let rename = editor.take_rename(cx)?;
4220 let buffer = editor.buffer.read(cx);
4221 let (start_buffer, start) =
4222 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4223 let (end_buffer, end) =
4224 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4225 if start_buffer == end_buffer {
4226 let new_name = rename.editor.read(cx).text(cx);
4227 Some((start_buffer, start..end, rename.old_name, new_name))
4228 } else {
4229 None
4230 }
4231 })?;
4232
4233 let rename = workspace.project().clone().update(cx, |project, cx| {
4234 project.perform_rename(
4235 buffer.clone(),
4236 range.start.clone(),
4237 new_name.clone(),
4238 true,
4239 cx,
4240 )
4241 });
4242
4243 Some(cx.spawn(|workspace, cx| async move {
4244 let project_transaction = rename.await?;
4245 Self::open_project_transaction(
4246 editor,
4247 workspace,
4248 project_transaction,
4249 format!("Rename: {} → {}", old_name, new_name),
4250 cx,
4251 )
4252 .await
4253 }))
4254 }
4255
4256 fn take_rename(&mut self, cx: &mut ViewContext<Self>) -> Option<RenameState> {
4257 let rename = self.pending_rename.take()?;
4258 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4259 self.clear_highlighted_ranges::<Rename>(cx);
4260
4261 let editor = rename.editor.read(cx);
4262 let buffer = editor.buffer.read(cx).snapshot(cx);
4263 let selection = editor.newest_selection::<usize>(&buffer);
4264
4265 // Update the selection to match the position of the selection inside
4266 // the rename editor.
4267 let snapshot = self.buffer.read(cx).snapshot(cx);
4268 let rename_range = rename.range.to_offset(&snapshot);
4269 let start = snapshot
4270 .clip_offset(rename_range.start + selection.start, Bias::Left)
4271 .min(rename_range.end);
4272 let end = snapshot
4273 .clip_offset(rename_range.start + selection.end, Bias::Left)
4274 .min(rename_range.end);
4275 self.update_selections(
4276 vec![Selection {
4277 id: self.newest_anchor_selection().id,
4278 start,
4279 end,
4280 reversed: selection.reversed,
4281 goal: SelectionGoal::None,
4282 }],
4283 None,
4284 cx,
4285 );
4286
4287 Some(rename)
4288 }
4289
4290 fn invalidate_rename_range(
4291 &mut self,
4292 buffer: &MultiBufferSnapshot,
4293 cx: &mut ViewContext<Self>,
4294 ) {
4295 if let Some(rename) = self.pending_rename.as_ref() {
4296 if self.selections.len() == 1 {
4297 let head = self.selections[0].head().to_offset(buffer);
4298 let range = rename.range.to_offset(buffer).to_inclusive();
4299 if range.contains(&head) {
4300 return;
4301 }
4302 }
4303 let rename = self.pending_rename.take().unwrap();
4304 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4305 self.clear_highlighted_ranges::<Rename>(cx);
4306 }
4307 }
4308
4309 #[cfg(any(test, feature = "test-support"))]
4310 pub fn pending_rename(&self) -> Option<&RenameState> {
4311 self.pending_rename.as_ref()
4312 }
4313
4314 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4315 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4316 let buffer = self.buffer.read(cx).snapshot(cx);
4317 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4318 let is_valid = buffer
4319 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
4320 .any(|entry| {
4321 entry.diagnostic.is_primary
4322 && !entry.range.is_empty()
4323 && entry.range.start == primary_range_start
4324 && entry.diagnostic.message == active_diagnostics.primary_message
4325 });
4326
4327 if is_valid != active_diagnostics.is_valid {
4328 active_diagnostics.is_valid = is_valid;
4329 let mut new_styles = HashMap::default();
4330 for (block_id, diagnostic) in &active_diagnostics.blocks {
4331 new_styles.insert(
4332 *block_id,
4333 diagnostic_block_renderer(
4334 diagnostic.clone(),
4335 is_valid,
4336 self.build_settings.clone(),
4337 ),
4338 );
4339 }
4340 self.display_map
4341 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
4342 }
4343 }
4344 }
4345
4346 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
4347 self.dismiss_diagnostics(cx);
4348 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
4349 let buffer = self.buffer.read(cx).snapshot(cx);
4350
4351 let mut primary_range = None;
4352 let mut primary_message = None;
4353 let mut group_end = Point::zero();
4354 let diagnostic_group = buffer
4355 .diagnostic_group::<Point>(group_id)
4356 .map(|entry| {
4357 if entry.range.end > group_end {
4358 group_end = entry.range.end;
4359 }
4360 if entry.diagnostic.is_primary {
4361 primary_range = Some(entry.range.clone());
4362 primary_message = Some(entry.diagnostic.message.clone());
4363 }
4364 entry
4365 })
4366 .collect::<Vec<_>>();
4367 let primary_range = primary_range.unwrap();
4368 let primary_message = primary_message.unwrap();
4369 let primary_range =
4370 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
4371
4372 let blocks = display_map
4373 .insert_blocks(
4374 diagnostic_group.iter().map(|entry| {
4375 let build_settings = self.build_settings.clone();
4376 let diagnostic = entry.diagnostic.clone();
4377 let message_height = diagnostic.message.lines().count() as u8;
4378
4379 BlockProperties {
4380 position: buffer.anchor_after(entry.range.start),
4381 height: message_height,
4382 render: diagnostic_block_renderer(diagnostic, true, build_settings),
4383 disposition: BlockDisposition::Below,
4384 }
4385 }),
4386 cx,
4387 )
4388 .into_iter()
4389 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
4390 .collect();
4391
4392 Some(ActiveDiagnosticGroup {
4393 primary_range,
4394 primary_message,
4395 blocks,
4396 is_valid: true,
4397 })
4398 });
4399 }
4400
4401 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
4402 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
4403 self.display_map.update(cx, |display_map, cx| {
4404 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
4405 });
4406 cx.notify();
4407 }
4408 }
4409
4410 fn build_columnar_selection(
4411 &mut self,
4412 display_map: &DisplaySnapshot,
4413 row: u32,
4414 columns: &Range<u32>,
4415 reversed: bool,
4416 ) -> Option<Selection<Point>> {
4417 let is_empty = columns.start == columns.end;
4418 let line_len = display_map.line_len(row);
4419 if columns.start < line_len || (is_empty && columns.start == line_len) {
4420 let start = DisplayPoint::new(row, columns.start);
4421 let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
4422 Some(Selection {
4423 id: post_inc(&mut self.next_selection_id),
4424 start: start.to_point(display_map),
4425 end: end.to_point(display_map),
4426 reversed,
4427 goal: SelectionGoal::ColumnRange {
4428 start: columns.start,
4429 end: columns.end,
4430 },
4431 })
4432 } else {
4433 None
4434 }
4435 }
4436
4437 pub fn local_selections_in_range(
4438 &self,
4439 range: Range<Anchor>,
4440 display_map: &DisplaySnapshot,
4441 ) -> Vec<Selection<Point>> {
4442 let buffer = &display_map.buffer_snapshot;
4443
4444 let start_ix = match self
4445 .selections
4446 .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer).unwrap())
4447 {
4448 Ok(ix) | Err(ix) => ix,
4449 };
4450 let end_ix = match self
4451 .selections
4452 .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer).unwrap())
4453 {
4454 Ok(ix) => ix + 1,
4455 Err(ix) => ix,
4456 };
4457
4458 fn point_selection(
4459 selection: &Selection<Anchor>,
4460 buffer: &MultiBufferSnapshot,
4461 ) -> Selection<Point> {
4462 let start = selection.start.to_point(&buffer);
4463 let end = selection.end.to_point(&buffer);
4464 Selection {
4465 id: selection.id,
4466 start,
4467 end,
4468 reversed: selection.reversed,
4469 goal: selection.goal,
4470 }
4471 }
4472
4473 self.selections[start_ix..end_ix]
4474 .iter()
4475 .chain(
4476 self.pending_selection
4477 .as_ref()
4478 .map(|pending| &pending.selection),
4479 )
4480 .map(|s| point_selection(s, &buffer))
4481 .collect()
4482 }
4483
4484 pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
4485 where
4486 D: 'a + TextDimension + Ord + Sub<D, Output = D>,
4487 {
4488 let buffer = self.buffer.read(cx).snapshot(cx);
4489 let mut selections = self
4490 .resolve_selections::<D, _>(self.selections.iter(), &buffer)
4491 .peekable();
4492
4493 let mut pending_selection = self.pending_selection::<D>(&buffer);
4494
4495 iter::from_fn(move || {
4496 if let Some(pending) = pending_selection.as_mut() {
4497 while let Some(next_selection) = selections.peek() {
4498 if pending.start <= next_selection.end && pending.end >= next_selection.start {
4499 let next_selection = selections.next().unwrap();
4500 if next_selection.start < pending.start {
4501 pending.start = next_selection.start;
4502 }
4503 if next_selection.end > pending.end {
4504 pending.end = next_selection.end;
4505 }
4506 } else if next_selection.end < pending.start {
4507 return selections.next();
4508 } else {
4509 break;
4510 }
4511 }
4512
4513 pending_selection.take()
4514 } else {
4515 selections.next()
4516 }
4517 })
4518 .collect()
4519 }
4520
4521 fn resolve_selections<'a, D, I>(
4522 &self,
4523 selections: I,
4524 snapshot: &MultiBufferSnapshot,
4525 ) -> impl 'a + Iterator<Item = Selection<D>>
4526 where
4527 D: TextDimension + Ord + Sub<D, Output = D>,
4528 I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
4529 {
4530 let (to_summarize, selections) = selections.into_iter().tee();
4531 let mut summaries = snapshot
4532 .summaries_for_anchors::<D, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
4533 .into_iter();
4534 selections.map(move |s| Selection {
4535 id: s.id,
4536 start: summaries.next().unwrap(),
4537 end: summaries.next().unwrap(),
4538 reversed: s.reversed,
4539 goal: s.goal,
4540 })
4541 }
4542
4543 fn pending_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
4544 &self,
4545 snapshot: &MultiBufferSnapshot,
4546 ) -> Option<Selection<D>> {
4547 self.pending_selection
4548 .as_ref()
4549 .map(|pending| self.resolve_selection(&pending.selection, &snapshot))
4550 }
4551
4552 fn resolve_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
4553 &self,
4554 selection: &Selection<Anchor>,
4555 buffer: &MultiBufferSnapshot,
4556 ) -> Selection<D> {
4557 Selection {
4558 id: selection.id,
4559 start: selection.start.summary::<D>(&buffer),
4560 end: selection.end.summary::<D>(&buffer),
4561 reversed: selection.reversed,
4562 goal: selection.goal,
4563 }
4564 }
4565
4566 fn selection_count<'a>(&self) -> usize {
4567 let mut count = self.selections.len();
4568 if self.pending_selection.is_some() {
4569 count += 1;
4570 }
4571 count
4572 }
4573
4574 pub fn oldest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
4575 &self,
4576 snapshot: &MultiBufferSnapshot,
4577 ) -> Selection<D> {
4578 self.selections
4579 .iter()
4580 .min_by_key(|s| s.id)
4581 .map(|selection| self.resolve_selection(selection, snapshot))
4582 .or_else(|| self.pending_selection(snapshot))
4583 .unwrap()
4584 }
4585
4586 pub fn newest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
4587 &self,
4588 snapshot: &MultiBufferSnapshot,
4589 ) -> Selection<D> {
4590 self.resolve_selection(self.newest_anchor_selection(), snapshot)
4591 }
4592
4593 pub fn newest_anchor_selection(&self) -> &Selection<Anchor> {
4594 self.pending_selection
4595 .as_ref()
4596 .map(|s| &s.selection)
4597 .or_else(|| self.selections.iter().max_by_key(|s| s.id))
4598 .unwrap()
4599 }
4600
4601 pub fn update_selections<T>(
4602 &mut self,
4603 mut selections: Vec<Selection<T>>,
4604 autoscroll: Option<Autoscroll>,
4605 cx: &mut ViewContext<Self>,
4606 ) where
4607 T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
4608 {
4609 let buffer = self.buffer.read(cx).snapshot(cx);
4610 selections.sort_unstable_by_key(|s| s.start);
4611
4612 // Merge overlapping selections.
4613 let mut i = 1;
4614 while i < selections.len() {
4615 if selections[i - 1].end >= selections[i].start {
4616 let removed = selections.remove(i);
4617 if removed.start < selections[i - 1].start {
4618 selections[i - 1].start = removed.start;
4619 }
4620 if removed.end > selections[i - 1].end {
4621 selections[i - 1].end = removed.end;
4622 }
4623 } else {
4624 i += 1;
4625 }
4626 }
4627
4628 if let Some(autoscroll) = autoscroll {
4629 self.request_autoscroll(autoscroll, cx);
4630 }
4631
4632 self.set_selections(
4633 Arc::from_iter(selections.into_iter().map(|selection| {
4634 let end_bias = if selection.end > selection.start {
4635 Bias::Left
4636 } else {
4637 Bias::Right
4638 };
4639 Selection {
4640 id: selection.id,
4641 start: buffer.anchor_after(selection.start),
4642 end: buffer.anchor_at(selection.end, end_bias),
4643 reversed: selection.reversed,
4644 goal: selection.goal,
4645 }
4646 })),
4647 None,
4648 cx,
4649 );
4650 }
4651
4652 /// Compute new ranges for any selections that were located in excerpts that have
4653 /// since been removed.
4654 ///
4655 /// Returns a `HashMap` indicating which selections whose former head position
4656 /// was no longer present. The keys of the map are selection ids. The values are
4657 /// the id of the new excerpt where the head of the selection has been moved.
4658 pub fn refresh_selections(&mut self, cx: &mut ViewContext<Self>) -> HashMap<usize, ExcerptId> {
4659 let snapshot = self.buffer.read(cx).read(cx);
4660 let anchors_with_status = snapshot.refresh_anchors(
4661 self.selections
4662 .iter()
4663 .flat_map(|selection| [&selection.start, &selection.end]),
4664 );
4665 let offsets =
4666 snapshot.summaries_for_anchors::<usize, _>(anchors_with_status.iter().map(|a| &a.1));
4667 let offsets = offsets.chunks(2);
4668 let statuses = anchors_with_status
4669 .chunks(2)
4670 .map(|a| (a[0].0 / 2, a[0].2, a[1].2));
4671
4672 let mut selections_with_lost_position = HashMap::default();
4673 let new_selections = offsets
4674 .zip(statuses)
4675 .map(|(offsets, (selection_ix, kept_start, kept_end))| {
4676 let selection = &self.selections[selection_ix];
4677 let kept_head = if selection.reversed {
4678 kept_start
4679 } else {
4680 kept_end
4681 };
4682 if !kept_head {
4683 selections_with_lost_position
4684 .insert(selection.id, selection.head().excerpt_id.clone());
4685 }
4686
4687 Selection {
4688 id: selection.id,
4689 start: offsets[0],
4690 end: offsets[1],
4691 reversed: selection.reversed,
4692 goal: selection.goal,
4693 }
4694 })
4695 .collect();
4696 drop(snapshot);
4697 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
4698 selections_with_lost_position
4699 }
4700
4701 fn set_selections(
4702 &mut self,
4703 selections: Arc<[Selection<Anchor>]>,
4704 pending_selection: Option<PendingSelection>,
4705 cx: &mut ViewContext<Self>,
4706 ) {
4707 let old_cursor_position = self.newest_anchor_selection().head();
4708
4709 self.selections = selections;
4710 self.pending_selection = pending_selection;
4711 if self.focused {
4712 self.buffer.update(cx, |buffer, cx| {
4713 buffer.set_active_selections(&self.selections, cx)
4714 });
4715 }
4716
4717 let display_map = self
4718 .display_map
4719 .update(cx, |display_map, cx| display_map.snapshot(cx));
4720 let buffer = &display_map.buffer_snapshot;
4721 self.add_selections_state = None;
4722 self.select_next_state = None;
4723 self.select_larger_syntax_node_stack.clear();
4724 self.autoclose_stack.invalidate(&self.selections, &buffer);
4725 self.snippet_stack.invalidate(&self.selections, &buffer);
4726 self.invalidate_rename_range(&buffer, cx);
4727
4728 let new_cursor_position = self.newest_anchor_selection().head();
4729
4730 self.push_to_nav_history(
4731 old_cursor_position.clone(),
4732 Some(new_cursor_position.to_point(&buffer)),
4733 cx,
4734 );
4735
4736 let completion_menu = match self.context_menu.as_mut() {
4737 Some(ContextMenu::Completions(menu)) => Some(menu),
4738 _ => {
4739 self.context_menu.take();
4740 None
4741 }
4742 };
4743
4744 if let Some(completion_menu) = completion_menu {
4745 let cursor_position = new_cursor_position.to_offset(&buffer);
4746 let (word_range, kind) =
4747 buffer.surrounding_word(completion_menu.initial_position.clone());
4748 if kind == Some(CharKind::Word) && word_range.to_inclusive().contains(&cursor_position)
4749 {
4750 let query = Self::completion_query(&buffer, cursor_position);
4751 cx.background()
4752 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
4753 self.show_completions(&ShowCompletions, cx);
4754 } else {
4755 self.hide_context_menu(cx);
4756 }
4757 }
4758
4759 if old_cursor_position.to_display_point(&display_map).row()
4760 != new_cursor_position.to_display_point(&display_map).row()
4761 {
4762 self.available_code_actions.take();
4763 }
4764 self.refresh_code_actions(cx);
4765
4766 self.pause_cursor_blinking(cx);
4767 cx.emit(Event::SelectionsChanged);
4768 }
4769
4770 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
4771 self.autoscroll_request = Some(autoscroll);
4772 cx.notify();
4773 }
4774
4775 fn start_transaction(&mut self, cx: &mut ViewContext<Self>) {
4776 self.start_transaction_at(Instant::now(), cx);
4777 }
4778
4779 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
4780 self.end_selection(cx);
4781 if let Some(tx_id) = self
4782 .buffer
4783 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
4784 {
4785 self.selection_history
4786 .insert(tx_id, (self.selections.clone(), None));
4787 }
4788 }
4789
4790 fn end_transaction(&mut self, cx: &mut ViewContext<Self>) {
4791 self.end_transaction_at(Instant::now(), cx);
4792 }
4793
4794 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
4795 if let Some(tx_id) = self
4796 .buffer
4797 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
4798 {
4799 if let Some((_, end_selections)) = self.selection_history.get_mut(&tx_id) {
4800 *end_selections = Some(self.selections.clone());
4801 } else {
4802 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
4803 }
4804 }
4805 }
4806
4807 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
4808 log::info!("Editor::page_up");
4809 }
4810
4811 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
4812 log::info!("Editor::page_down");
4813 }
4814
4815 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
4816 let mut fold_ranges = Vec::new();
4817
4818 let selections = self.local_selections::<Point>(cx);
4819 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4820 for selection in selections {
4821 let range = selection.display_range(&display_map).sorted();
4822 let buffer_start_row = range.start.to_point(&display_map).row;
4823
4824 for row in (0..=range.end.row()).rev() {
4825 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
4826 let fold_range = self.foldable_range_for_line(&display_map, row);
4827 if fold_range.end.row >= buffer_start_row {
4828 fold_ranges.push(fold_range);
4829 if row <= range.start.row() {
4830 break;
4831 }
4832 }
4833 }
4834 }
4835 }
4836
4837 self.fold_ranges(fold_ranges, cx);
4838 }
4839
4840 pub fn unfold(&mut self, _: &Unfold, cx: &mut ViewContext<Self>) {
4841 let selections = self.local_selections::<Point>(cx);
4842 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4843 let buffer = &display_map.buffer_snapshot;
4844 let ranges = selections
4845 .iter()
4846 .map(|s| {
4847 let range = s.display_range(&display_map).sorted();
4848 let mut start = range.start.to_point(&display_map);
4849 let mut end = range.end.to_point(&display_map);
4850 start.column = 0;
4851 end.column = buffer.line_len(end.row);
4852 start..end
4853 })
4854 .collect::<Vec<_>>();
4855 self.unfold_ranges(ranges, cx);
4856 }
4857
4858 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
4859 let max_point = display_map.max_point();
4860 if display_row >= max_point.row() {
4861 false
4862 } else {
4863 let (start_indent, is_blank) = display_map.line_indent(display_row);
4864 if is_blank {
4865 false
4866 } else {
4867 for display_row in display_row + 1..=max_point.row() {
4868 let (indent, is_blank) = display_map.line_indent(display_row);
4869 if !is_blank {
4870 return indent > start_indent;
4871 }
4872 }
4873 false
4874 }
4875 }
4876 }
4877
4878 fn foldable_range_for_line(
4879 &self,
4880 display_map: &DisplaySnapshot,
4881 start_row: u32,
4882 ) -> Range<Point> {
4883 let max_point = display_map.max_point();
4884
4885 let (start_indent, _) = display_map.line_indent(start_row);
4886 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
4887 let mut end = None;
4888 for row in start_row + 1..=max_point.row() {
4889 let (indent, is_blank) = display_map.line_indent(row);
4890 if !is_blank && indent <= start_indent {
4891 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
4892 break;
4893 }
4894 }
4895
4896 let end = end.unwrap_or(max_point);
4897 return start.to_point(display_map)..end.to_point(display_map);
4898 }
4899
4900 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
4901 let selections = self.local_selections::<Point>(cx);
4902 let ranges = selections.into_iter().map(|s| s.start..s.end);
4903 self.fold_ranges(ranges, cx);
4904 }
4905
4906 fn fold_ranges<T: ToOffset>(
4907 &mut self,
4908 ranges: impl IntoIterator<Item = Range<T>>,
4909 cx: &mut ViewContext<Self>,
4910 ) {
4911 let mut ranges = ranges.into_iter().peekable();
4912 if ranges.peek().is_some() {
4913 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
4914 self.request_autoscroll(Autoscroll::Fit, cx);
4915 cx.notify();
4916 }
4917 }
4918
4919 fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, cx: &mut ViewContext<Self>) {
4920 if !ranges.is_empty() {
4921 self.display_map
4922 .update(cx, |map, cx| map.unfold(ranges, cx));
4923 self.request_autoscroll(Autoscroll::Fit, cx);
4924 cx.notify();
4925 }
4926 }
4927
4928 pub fn insert_blocks(
4929 &mut self,
4930 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
4931 cx: &mut ViewContext<Self>,
4932 ) -> Vec<BlockId> {
4933 let blocks = self
4934 .display_map
4935 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
4936 self.request_autoscroll(Autoscroll::Fit, cx);
4937 blocks
4938 }
4939
4940 pub fn replace_blocks(
4941 &mut self,
4942 blocks: HashMap<BlockId, RenderBlock>,
4943 cx: &mut ViewContext<Self>,
4944 ) {
4945 self.display_map
4946 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
4947 self.request_autoscroll(Autoscroll::Fit, cx);
4948 }
4949
4950 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
4951 self.display_map.update(cx, |display_map, cx| {
4952 display_map.remove_blocks(block_ids, cx)
4953 });
4954 }
4955
4956 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
4957 self.display_map
4958 .update(cx, |map, cx| map.snapshot(cx))
4959 .longest_row()
4960 }
4961
4962 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
4963 self.display_map
4964 .update(cx, |map, cx| map.snapshot(cx))
4965 .max_point()
4966 }
4967
4968 pub fn text(&self, cx: &AppContext) -> String {
4969 self.buffer.read(cx).read(cx).text()
4970 }
4971
4972 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
4973 self.display_map
4974 .update(cx, |map, cx| map.snapshot(cx))
4975 .text()
4976 }
4977
4978 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
4979 self.display_map
4980 .update(cx, |map, cx| map.set_wrap_width(width, cx))
4981 }
4982
4983 pub fn set_highlighted_rows(&mut self, rows: Option<Range<u32>>) {
4984 self.highlighted_rows = rows;
4985 }
4986
4987 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
4988 self.highlighted_rows.clone()
4989 }
4990
4991 pub fn highlight_ranges<T: 'static>(
4992 &mut self,
4993 ranges: Vec<Range<Anchor>>,
4994 color: Color,
4995 cx: &mut ViewContext<Self>,
4996 ) {
4997 self.highlighted_ranges
4998 .insert(TypeId::of::<T>(), (color, ranges));
4999 cx.notify();
5000 }
5001
5002 pub fn clear_highlighted_ranges<T: 'static>(
5003 &mut self,
5004 cx: &mut ViewContext<Self>,
5005 ) -> Option<(Color, Vec<Range<Anchor>>)> {
5006 cx.notify();
5007 self.highlighted_ranges.remove(&TypeId::of::<T>())
5008 }
5009
5010 #[cfg(feature = "test-support")]
5011 pub fn all_highlighted_ranges(
5012 &mut self,
5013 cx: &mut ViewContext<Self>,
5014 ) -> Vec<(Range<DisplayPoint>, Color)> {
5015 let snapshot = self.snapshot(cx);
5016 let buffer = &snapshot.buffer_snapshot;
5017 let start = buffer.anchor_before(0);
5018 let end = buffer.anchor_after(buffer.len());
5019 self.highlighted_ranges_in_range(start..end, &snapshot)
5020 }
5021
5022 pub fn highlighted_ranges_for_type<T: 'static>(&self) -> Option<(Color, &[Range<Anchor>])> {
5023 self.highlighted_ranges
5024 .get(&TypeId::of::<T>())
5025 .map(|(color, ranges)| (*color, ranges.as_slice()))
5026 }
5027
5028 pub fn highlighted_ranges_in_range(
5029 &self,
5030 search_range: Range<Anchor>,
5031 display_snapshot: &DisplaySnapshot,
5032 ) -> Vec<(Range<DisplayPoint>, Color)> {
5033 let mut results = Vec::new();
5034 let buffer = &display_snapshot.buffer_snapshot;
5035 for (color, ranges) in self.highlighted_ranges.values() {
5036 let start_ix = match ranges.binary_search_by(|probe| {
5037 let cmp = probe.end.cmp(&search_range.start, &buffer).unwrap();
5038 if cmp.is_gt() {
5039 Ordering::Greater
5040 } else {
5041 Ordering::Less
5042 }
5043 }) {
5044 Ok(i) | Err(i) => i,
5045 };
5046 for range in &ranges[start_ix..] {
5047 if range.start.cmp(&search_range.end, &buffer).unwrap().is_ge() {
5048 break;
5049 }
5050 let start = range
5051 .start
5052 .to_point(buffer)
5053 .to_display_point(display_snapshot);
5054 let end = range
5055 .end
5056 .to_point(buffer)
5057 .to_display_point(display_snapshot);
5058 results.push((start..end, *color))
5059 }
5060 }
5061 results
5062 }
5063
5064 fn next_blink_epoch(&mut self) -> usize {
5065 self.blink_epoch += 1;
5066 self.blink_epoch
5067 }
5068
5069 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5070 if !self.focused {
5071 return;
5072 }
5073
5074 self.show_local_cursors = true;
5075 cx.notify();
5076
5077 let epoch = self.next_blink_epoch();
5078 cx.spawn(|this, mut cx| {
5079 let this = this.downgrade();
5080 async move {
5081 Timer::after(CURSOR_BLINK_INTERVAL).await;
5082 if let Some(this) = this.upgrade(&cx) {
5083 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5084 }
5085 }
5086 })
5087 .detach();
5088 }
5089
5090 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5091 if epoch == self.blink_epoch {
5092 self.blinking_paused = false;
5093 self.blink_cursors(epoch, cx);
5094 }
5095 }
5096
5097 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5098 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5099 self.show_local_cursors = !self.show_local_cursors;
5100 cx.notify();
5101
5102 let epoch = self.next_blink_epoch();
5103 cx.spawn(|this, mut cx| {
5104 let this = this.downgrade();
5105 async move {
5106 Timer::after(CURSOR_BLINK_INTERVAL).await;
5107 if let Some(this) = this.upgrade(&cx) {
5108 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5109 }
5110 }
5111 })
5112 .detach();
5113 }
5114 }
5115
5116 pub fn show_local_cursors(&self) -> bool {
5117 self.show_local_cursors
5118 }
5119
5120 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5121 cx.notify();
5122 }
5123
5124 fn on_buffer_event(
5125 &mut self,
5126 _: ModelHandle<MultiBuffer>,
5127 event: &language::Event,
5128 cx: &mut ViewContext<Self>,
5129 ) {
5130 match event {
5131 language::Event::Edited => {
5132 self.refresh_active_diagnostics(cx);
5133 self.refresh_code_actions(cx);
5134 cx.emit(Event::Edited);
5135 }
5136 language::Event::Dirtied => cx.emit(Event::Dirtied),
5137 language::Event::Saved => cx.emit(Event::Saved),
5138 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5139 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5140 language::Event::Closed => cx.emit(Event::Closed),
5141 language::Event::DiagnosticsUpdated => {
5142 self.refresh_active_diagnostics(cx);
5143 }
5144 _ => {}
5145 }
5146 }
5147
5148 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5149 cx.notify();
5150 }
5151}
5152
5153impl EditorSnapshot {
5154 pub fn is_focused(&self) -> bool {
5155 self.is_focused
5156 }
5157
5158 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5159 self.placeholder_text.as_ref()
5160 }
5161
5162 pub fn scroll_position(&self) -> Vector2F {
5163 compute_scroll_position(
5164 &self.display_snapshot,
5165 self.scroll_position,
5166 &self.scroll_top_anchor,
5167 )
5168 }
5169}
5170
5171impl Deref for EditorSnapshot {
5172 type Target = DisplaySnapshot;
5173
5174 fn deref(&self) -> &Self::Target {
5175 &self.display_snapshot
5176 }
5177}
5178
5179impl EditorSettings {
5180 #[cfg(any(test, feature = "test-support"))]
5181 pub fn test(cx: &AppContext) -> Self {
5182 use theme::{ContainedLabel, ContainedText, DiagnosticHeader, DiagnosticPathHeader};
5183
5184 Self {
5185 tab_size: 4,
5186 soft_wrap: SoftWrap::None,
5187 style: {
5188 let font_cache: &gpui::FontCache = cx.font_cache();
5189 let font_family_name = Arc::from("Monaco");
5190 let font_properties = Default::default();
5191 let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap();
5192 let font_id = font_cache
5193 .select_font(font_family_id, &font_properties)
5194 .unwrap();
5195 let text = gpui::fonts::TextStyle {
5196 font_family_name,
5197 font_family_id,
5198 font_id,
5199 font_size: 14.,
5200 color: gpui::color::Color::from_u32(0xff0000ff),
5201 font_properties,
5202 underline: None,
5203 };
5204 let default_diagnostic_style = DiagnosticStyle {
5205 message: text.clone().into(),
5206 header: Default::default(),
5207 text_scale_factor: 1.,
5208 };
5209 EditorStyle {
5210 text: text.clone(),
5211 placeholder_text: None,
5212 background: Default::default(),
5213 gutter_background: Default::default(),
5214 gutter_padding_factor: 2.,
5215 active_line_background: Default::default(),
5216 highlighted_line_background: Default::default(),
5217 diff_background_deleted: Default::default(),
5218 diff_background_inserted: Default::default(),
5219 line_number: Default::default(),
5220 line_number_active: Default::default(),
5221 selection: Default::default(),
5222 guest_selections: Default::default(),
5223 syntax: Default::default(),
5224 diagnostic_path_header: DiagnosticPathHeader {
5225 container: Default::default(),
5226 filename: ContainedText {
5227 container: Default::default(),
5228 text: text.clone(),
5229 },
5230 path: ContainedText {
5231 container: Default::default(),
5232 text: text.clone(),
5233 },
5234 text_scale_factor: 1.,
5235 },
5236 diagnostic_header: DiagnosticHeader {
5237 container: Default::default(),
5238 message: ContainedLabel {
5239 container: Default::default(),
5240 label: text.clone().into(),
5241 },
5242 code: ContainedText {
5243 container: Default::default(),
5244 text: text.clone(),
5245 },
5246 icon_width_factor: 1.,
5247 text_scale_factor: 1.,
5248 },
5249 error_diagnostic: default_diagnostic_style.clone(),
5250 invalid_error_diagnostic: default_diagnostic_style.clone(),
5251 warning_diagnostic: default_diagnostic_style.clone(),
5252 invalid_warning_diagnostic: default_diagnostic_style.clone(),
5253 information_diagnostic: default_diagnostic_style.clone(),
5254 invalid_information_diagnostic: default_diagnostic_style.clone(),
5255 hint_diagnostic: default_diagnostic_style.clone(),
5256 invalid_hint_diagnostic: default_diagnostic_style.clone(),
5257 autocomplete: Default::default(),
5258 code_actions_indicator: Default::default(),
5259 }
5260 },
5261 }
5262 }
5263}
5264
5265fn compute_scroll_position(
5266 snapshot: &DisplaySnapshot,
5267 mut scroll_position: Vector2F,
5268 scroll_top_anchor: &Option<Anchor>,
5269) -> Vector2F {
5270 if let Some(anchor) = scroll_top_anchor {
5271 let scroll_top = anchor.to_display_point(snapshot).row() as f32;
5272 scroll_position.set_y(scroll_top + scroll_position.y());
5273 } else {
5274 scroll_position.set_y(0.);
5275 }
5276 scroll_position
5277}
5278
5279#[derive(Copy, Clone)]
5280pub enum Event {
5281 Activate,
5282 Edited,
5283 Blurred,
5284 Dirtied,
5285 Saved,
5286 TitleChanged,
5287 SelectionsChanged,
5288 Closed,
5289}
5290
5291impl Entity for Editor {
5292 type Event = Event;
5293}
5294
5295impl View for Editor {
5296 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5297 let settings = (self.build_settings)(cx);
5298 self.display_map.update(cx, |map, cx| {
5299 map.set_font(
5300 settings.style.text.font_id,
5301 settings.style.text.font_size,
5302 cx,
5303 )
5304 });
5305 EditorElement::new(self.handle.clone(), settings).boxed()
5306 }
5307
5308 fn ui_name() -> &'static str {
5309 "Editor"
5310 }
5311
5312 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5313 self.focused = true;
5314 self.blink_cursors(self.blink_epoch, cx);
5315 self.buffer.update(cx, |buffer, cx| {
5316 buffer.finalize_last_transaction(cx);
5317 buffer.set_active_selections(&self.selections, cx)
5318 });
5319 }
5320
5321 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5322 self.focused = false;
5323 self.show_local_cursors = false;
5324 self.buffer
5325 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5326 self.hide_context_menu(cx);
5327 cx.emit(Event::Blurred);
5328 cx.notify();
5329 }
5330
5331 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5332 let mut cx = Self::default_keymap_context();
5333 let mode = match self.mode {
5334 EditorMode::SingleLine => "single_line",
5335 EditorMode::AutoHeight { .. } => "auto_height",
5336 EditorMode::Full => "full",
5337 };
5338 cx.map.insert("mode".into(), mode.into());
5339 if self.pending_rename.is_some() {
5340 cx.set.insert("renaming".into());
5341 }
5342 match self.context_menu.as_ref() {
5343 Some(ContextMenu::Completions(_)) => {
5344 cx.set.insert("showing_completions".into());
5345 }
5346 Some(ContextMenu::CodeActions(_)) => {
5347 cx.set.insert("showing_code_actions".into());
5348 }
5349 None => {}
5350 }
5351 cx
5352 }
5353}
5354
5355impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5356 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5357 let start = self.start.to_point(buffer);
5358 let end = self.end.to_point(buffer);
5359 if self.reversed {
5360 end..start
5361 } else {
5362 start..end
5363 }
5364 }
5365
5366 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5367 let start = self.start.to_offset(buffer);
5368 let end = self.end.to_offset(buffer);
5369 if self.reversed {
5370 end..start
5371 } else {
5372 start..end
5373 }
5374 }
5375
5376 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5377 let start = self
5378 .start
5379 .to_point(&map.buffer_snapshot)
5380 .to_display_point(map);
5381 let end = self
5382 .end
5383 .to_point(&map.buffer_snapshot)
5384 .to_display_point(map);
5385 if self.reversed {
5386 end..start
5387 } else {
5388 start..end
5389 }
5390 }
5391
5392 fn spanned_rows(
5393 &self,
5394 include_end_if_at_line_start: bool,
5395 map: &DisplaySnapshot,
5396 ) -> Range<u32> {
5397 let start = self.start.to_point(&map.buffer_snapshot);
5398 let mut end = self.end.to_point(&map.buffer_snapshot);
5399 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5400 end.row -= 1;
5401 }
5402
5403 let buffer_start = map.prev_line_boundary(start).0;
5404 let buffer_end = map.next_line_boundary(end).0;
5405 buffer_start.row..buffer_end.row + 1
5406 }
5407}
5408
5409impl<T: InvalidationRegion> InvalidationStack<T> {
5410 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5411 where
5412 S: Clone + ToOffset,
5413 {
5414 while let Some(region) = self.last() {
5415 let all_selections_inside_invalidation_ranges =
5416 if selections.len() == region.ranges().len() {
5417 selections
5418 .iter()
5419 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5420 .all(|(selection, invalidation_range)| {
5421 let head = selection.head().to_offset(&buffer);
5422 invalidation_range.start <= head && invalidation_range.end >= head
5423 })
5424 } else {
5425 false
5426 };
5427
5428 if all_selections_inside_invalidation_ranges {
5429 break;
5430 } else {
5431 self.pop();
5432 }
5433 }
5434 }
5435}
5436
5437impl<T> Default for InvalidationStack<T> {
5438 fn default() -> Self {
5439 Self(Default::default())
5440 }
5441}
5442
5443impl<T> Deref for InvalidationStack<T> {
5444 type Target = Vec<T>;
5445
5446 fn deref(&self) -> &Self::Target {
5447 &self.0
5448 }
5449}
5450
5451impl<T> DerefMut for InvalidationStack<T> {
5452 fn deref_mut(&mut self) -> &mut Self::Target {
5453 &mut self.0
5454 }
5455}
5456
5457impl InvalidationRegion for BracketPairState {
5458 fn ranges(&self) -> &[Range<Anchor>] {
5459 &self.ranges
5460 }
5461}
5462
5463impl InvalidationRegion for SnippetState {
5464 fn ranges(&self) -> &[Range<Anchor>] {
5465 &self.ranges[self.active_index]
5466 }
5467}
5468
5469pub fn diagnostic_block_renderer(
5470 diagnostic: Diagnostic,
5471 is_valid: bool,
5472 build_settings: BuildSettings,
5473) -> RenderBlock {
5474 let mut highlighted_lines = Vec::new();
5475 for line in diagnostic.message.lines() {
5476 highlighted_lines.push(highlight_diagnostic_message(line));
5477 }
5478
5479 Arc::new(move |cx: &BlockContext| {
5480 let settings = build_settings(cx);
5481 let style = diagnostic_style(diagnostic.severity, is_valid, &settings.style);
5482 let font_size = (style.text_scale_factor * settings.style.text.font_size).round();
5483 Flex::column()
5484 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
5485 Label::new(
5486 line.clone(),
5487 style.message.clone().with_font_size(font_size),
5488 )
5489 .with_highlights(highlights.clone())
5490 .contained()
5491 .with_margin_left(cx.anchor_x)
5492 .boxed()
5493 }))
5494 .aligned()
5495 .left()
5496 .boxed()
5497 })
5498}
5499
5500pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
5501 let mut message_without_backticks = String::new();
5502 let mut prev_offset = 0;
5503 let mut inside_block = false;
5504 let mut highlights = Vec::new();
5505 for (match_ix, (offset, _)) in message
5506 .match_indices('`')
5507 .chain([(message.len(), "")])
5508 .enumerate()
5509 {
5510 message_without_backticks.push_str(&message[prev_offset..offset]);
5511 if inside_block {
5512 highlights.extend(prev_offset - match_ix..offset - match_ix);
5513 }
5514
5515 inside_block = !inside_block;
5516 prev_offset = offset + 1;
5517 }
5518
5519 (message_without_backticks, highlights)
5520}
5521
5522pub fn diagnostic_style(
5523 severity: DiagnosticSeverity,
5524 valid: bool,
5525 style: &EditorStyle,
5526) -> DiagnosticStyle {
5527 match (severity, valid) {
5528 (DiagnosticSeverity::ERROR, true) => style.error_diagnostic.clone(),
5529 (DiagnosticSeverity::ERROR, false) => style.invalid_error_diagnostic.clone(),
5530 (DiagnosticSeverity::WARNING, true) => style.warning_diagnostic.clone(),
5531 (DiagnosticSeverity::WARNING, false) => style.invalid_warning_diagnostic.clone(),
5532 (DiagnosticSeverity::INFORMATION, true) => style.information_diagnostic.clone(),
5533 (DiagnosticSeverity::INFORMATION, false) => style.invalid_information_diagnostic.clone(),
5534 (DiagnosticSeverity::HINT, true) => style.hint_diagnostic.clone(),
5535 (DiagnosticSeverity::HINT, false) => style.invalid_hint_diagnostic.clone(),
5536 _ => DiagnosticStyle {
5537 message: style.text.clone().into(),
5538 header: Default::default(),
5539 text_scale_factor: 1.,
5540 },
5541 }
5542}
5543
5544pub fn settings_builder(
5545 buffer: WeakModelHandle<MultiBuffer>,
5546 settings: watch::Receiver<workspace::Settings>,
5547) -> BuildSettings {
5548 Arc::new(move |cx| {
5549 let settings = settings.borrow();
5550 let font_cache = cx.font_cache();
5551 let font_family_id = settings.buffer_font_family;
5552 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5553 let font_properties = Default::default();
5554 let font_id = font_cache
5555 .select_font(font_family_id, &font_properties)
5556 .unwrap();
5557 let font_size = settings.buffer_font_size;
5558
5559 let mut theme = settings.theme.editor.clone();
5560 theme.text = TextStyle {
5561 color: theme.text.color,
5562 font_family_name,
5563 font_family_id,
5564 font_id,
5565 font_size,
5566 font_properties,
5567 underline: None,
5568 };
5569 let language = buffer.upgrade(cx).and_then(|buf| buf.read(cx).language(cx));
5570 let soft_wrap = match settings.soft_wrap(language) {
5571 workspace::settings::SoftWrap::None => SoftWrap::None,
5572 workspace::settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5573 workspace::settings::SoftWrap::PreferredLineLength => {
5574 SoftWrap::Column(settings.preferred_line_length(language).saturating_sub(1))
5575 }
5576 };
5577
5578 EditorSettings {
5579 tab_size: settings.tab_size,
5580 soft_wrap,
5581 style: theme,
5582 }
5583 })
5584}
5585
5586pub fn combine_syntax_and_fuzzy_match_highlights(
5587 text: &str,
5588 default_style: HighlightStyle,
5589 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
5590 match_indices: &[usize],
5591) -> Vec<(Range<usize>, HighlightStyle)> {
5592 let mut result = Vec::new();
5593 let mut match_indices = match_indices.iter().copied().peekable();
5594
5595 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
5596 {
5597 syntax_highlight.font_properties.weight(Default::default());
5598
5599 // Add highlights for any fuzzy match characters before the next
5600 // syntax highlight range.
5601 while let Some(&match_index) = match_indices.peek() {
5602 if match_index >= range.start {
5603 break;
5604 }
5605 match_indices.next();
5606 let end_index = char_ix_after(match_index, text);
5607 let mut match_style = default_style;
5608 match_style.font_properties.weight(fonts::Weight::BOLD);
5609 result.push((match_index..end_index, match_style));
5610 }
5611
5612 if range.start == usize::MAX {
5613 break;
5614 }
5615
5616 // Add highlights for any fuzzy match characters within the
5617 // syntax highlight range.
5618 let mut offset = range.start;
5619 while let Some(&match_index) = match_indices.peek() {
5620 if match_index >= range.end {
5621 break;
5622 }
5623
5624 match_indices.next();
5625 if match_index > offset {
5626 result.push((offset..match_index, syntax_highlight));
5627 }
5628
5629 let mut end_index = char_ix_after(match_index, text);
5630 while let Some(&next_match_index) = match_indices.peek() {
5631 if next_match_index == end_index && next_match_index < range.end {
5632 end_index = char_ix_after(next_match_index, text);
5633 match_indices.next();
5634 } else {
5635 break;
5636 }
5637 }
5638
5639 let mut match_style = syntax_highlight;
5640 match_style.font_properties.weight(fonts::Weight::BOLD);
5641 result.push((match_index..end_index, match_style));
5642 offset = end_index;
5643 }
5644
5645 if offset < range.end {
5646 result.push((offset..range.end, syntax_highlight));
5647 }
5648 }
5649
5650 fn char_ix_after(ix: usize, text: &str) -> usize {
5651 ix + text[ix..].chars().next().unwrap().len_utf8()
5652 }
5653
5654 result
5655}
5656
5657pub fn styled_runs_for_code_label<'a>(
5658 label: &'a CodeLabel,
5659 default_color: Color,
5660 syntax_theme: &'a theme::SyntaxTheme,
5661) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
5662 const MUTED_OPACITY: usize = 165;
5663
5664 let mut muted_default_style = HighlightStyle {
5665 color: default_color,
5666 ..Default::default()
5667 };
5668 muted_default_style.color.a = ((default_color.a as usize * MUTED_OPACITY) / 255) as u8;
5669
5670 let mut prev_end = label.filter_range.end;
5671 label
5672 .runs
5673 .iter()
5674 .enumerate()
5675 .flat_map(move |(ix, (range, highlight_id))| {
5676 let style = if let Some(style) = highlight_id.style(syntax_theme) {
5677 style
5678 } else {
5679 return Default::default();
5680 };
5681 let mut muted_style = style.clone();
5682 muted_style.color.a = ((style.color.a as usize * MUTED_OPACITY) / 255) as u8;
5683
5684 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
5685 if range.start >= label.filter_range.end {
5686 if range.start > prev_end {
5687 runs.push((prev_end..range.start, muted_default_style));
5688 }
5689 runs.push((range.clone(), muted_style));
5690 } else if range.end <= label.filter_range.end {
5691 runs.push((range.clone(), style));
5692 } else {
5693 runs.push((range.start..label.filter_range.end, style));
5694 runs.push((label.filter_range.end..range.end, muted_style));
5695 }
5696 prev_end = cmp::max(prev_end, range.end);
5697
5698 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
5699 runs.push((prev_end..label.text.len(), muted_default_style));
5700 }
5701
5702 runs
5703 })
5704}
5705
5706#[cfg(test)]
5707mod tests {
5708 use super::*;
5709 use language::LanguageConfig;
5710 use lsp::FakeLanguageServer;
5711 use project::{FakeFs, ProjectPath};
5712 use smol::stream::StreamExt;
5713 use std::{cell::RefCell, rc::Rc, time::Instant};
5714 use text::Point;
5715 use unindent::Unindent;
5716 use util::test::sample_text;
5717
5718 #[gpui::test]
5719 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
5720 let mut now = Instant::now();
5721 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
5722 let group_interval = buffer.read(cx).transaction_group_interval();
5723 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5724 let settings = EditorSettings::test(cx);
5725 let (_, editor) = cx.add_window(Default::default(), |cx| {
5726 build_editor(buffer.clone(), settings, cx)
5727 });
5728
5729 editor.update(cx, |editor, cx| {
5730 editor.start_transaction_at(now, cx);
5731 editor.select_ranges([2..4], None, cx);
5732 editor.insert("cd", cx);
5733 editor.end_transaction_at(now, cx);
5734 assert_eq!(editor.text(cx), "12cd56");
5735 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
5736
5737 editor.start_transaction_at(now, cx);
5738 editor.select_ranges([4..5], None, cx);
5739 editor.insert("e", cx);
5740 editor.end_transaction_at(now, cx);
5741 assert_eq!(editor.text(cx), "12cde6");
5742 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
5743
5744 now += group_interval + Duration::from_millis(1);
5745 editor.select_ranges([2..2], None, cx);
5746
5747 // Simulate an edit in another editor
5748 buffer.update(cx, |buffer, cx| {
5749 buffer.start_transaction_at(now, cx);
5750 buffer.edit([0..1], "a", cx);
5751 buffer.edit([1..1], "b", cx);
5752 buffer.end_transaction_at(now, cx);
5753 });
5754
5755 assert_eq!(editor.text(cx), "ab2cde6");
5756 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
5757
5758 // Last transaction happened past the group interval in a different editor.
5759 // Undo it individually and don't restore selections.
5760 editor.undo(&Undo, cx);
5761 assert_eq!(editor.text(cx), "12cde6");
5762 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
5763
5764 // First two transactions happened within the group interval in this editor.
5765 // Undo them together and restore selections.
5766 editor.undo(&Undo, cx);
5767 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
5768 assert_eq!(editor.text(cx), "123456");
5769 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
5770
5771 // Redo the first two transactions together.
5772 editor.redo(&Redo, cx);
5773 assert_eq!(editor.text(cx), "12cde6");
5774 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
5775
5776 // Redo the last transaction on its own.
5777 editor.redo(&Redo, cx);
5778 assert_eq!(editor.text(cx), "ab2cde6");
5779 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
5780
5781 // Test empty transactions.
5782 editor.start_transaction_at(now, cx);
5783 editor.end_transaction_at(now, cx);
5784 editor.undo(&Undo, cx);
5785 assert_eq!(editor.text(cx), "12cde6");
5786 });
5787 }
5788
5789 #[gpui::test]
5790 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
5791 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
5792 let settings = EditorSettings::test(cx);
5793 let (_, editor) =
5794 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5795
5796 editor.update(cx, |view, cx| {
5797 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
5798 });
5799
5800 assert_eq!(
5801 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5802 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
5803 );
5804
5805 editor.update(cx, |view, cx| {
5806 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
5807 });
5808
5809 assert_eq!(
5810 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5811 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
5812 );
5813
5814 editor.update(cx, |view, cx| {
5815 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
5816 });
5817
5818 assert_eq!(
5819 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5820 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
5821 );
5822
5823 editor.update(cx, |view, cx| {
5824 view.end_selection(cx);
5825 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
5826 });
5827
5828 assert_eq!(
5829 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5830 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
5831 );
5832
5833 editor.update(cx, |view, cx| {
5834 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
5835 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
5836 });
5837
5838 assert_eq!(
5839 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5840 [
5841 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
5842 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
5843 ]
5844 );
5845
5846 editor.update(cx, |view, cx| {
5847 view.end_selection(cx);
5848 });
5849
5850 assert_eq!(
5851 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
5852 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
5853 );
5854 }
5855
5856 #[gpui::test]
5857 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
5858 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
5859 let settings = EditorSettings::test(cx);
5860 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5861
5862 view.update(cx, |view, cx| {
5863 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
5864 assert_eq!(
5865 view.selected_display_ranges(cx),
5866 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
5867 );
5868 });
5869
5870 view.update(cx, |view, cx| {
5871 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
5872 assert_eq!(
5873 view.selected_display_ranges(cx),
5874 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
5875 );
5876 });
5877
5878 view.update(cx, |view, cx| {
5879 view.cancel(&Cancel, cx);
5880 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
5881 assert_eq!(
5882 view.selected_display_ranges(cx),
5883 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
5884 );
5885 });
5886 }
5887
5888 #[gpui::test]
5889 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
5890 cx.add_window(Default::default(), |cx| {
5891 use workspace::ItemView;
5892 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
5893 let settings = EditorSettings::test(&cx);
5894 let buffer = MultiBuffer::build_simple(&sample_text(30, 5, 'a'), cx);
5895 let mut editor = build_editor(buffer.clone(), settings, cx);
5896 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
5897
5898 // Move the cursor a small distance.
5899 // Nothing is added to the navigation history.
5900 editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
5901 editor.select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx);
5902 assert!(nav_history.borrow_mut().pop_backward().is_none());
5903
5904 // Move the cursor a large distance.
5905 // The history can jump back to the previous position.
5906 editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx);
5907 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
5908 editor.navigate(nav_entry.data.unwrap(), cx);
5909 assert_eq!(nav_entry.item_view.id(), cx.view_id());
5910 assert_eq!(
5911 editor.selected_display_ranges(cx),
5912 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
5913 );
5914
5915 // Move the cursor a small distance via the mouse.
5916 // Nothing is added to the navigation history.
5917 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
5918 editor.end_selection(cx);
5919 assert_eq!(
5920 editor.selected_display_ranges(cx),
5921 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
5922 );
5923 assert!(nav_history.borrow_mut().pop_backward().is_none());
5924
5925 // Move the cursor a large distance via the mouse.
5926 // The history can jump back to the previous position.
5927 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
5928 editor.end_selection(cx);
5929 assert_eq!(
5930 editor.selected_display_ranges(cx),
5931 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
5932 );
5933 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
5934 editor.navigate(nav_entry.data.unwrap(), cx);
5935 assert_eq!(nav_entry.item_view.id(), cx.view_id());
5936 assert_eq!(
5937 editor.selected_display_ranges(cx),
5938 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
5939 );
5940
5941 editor
5942 });
5943 }
5944
5945 #[gpui::test]
5946 fn test_cancel(cx: &mut gpui::MutableAppContext) {
5947 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
5948 let settings = EditorSettings::test(cx);
5949 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5950
5951 view.update(cx, |view, cx| {
5952 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
5953 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
5954 view.end_selection(cx);
5955
5956 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
5957 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
5958 view.end_selection(cx);
5959 assert_eq!(
5960 view.selected_display_ranges(cx),
5961 [
5962 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5963 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
5964 ]
5965 );
5966 });
5967
5968 view.update(cx, |view, cx| {
5969 view.cancel(&Cancel, cx);
5970 assert_eq!(
5971 view.selected_display_ranges(cx),
5972 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
5973 );
5974 });
5975
5976 view.update(cx, |view, cx| {
5977 view.cancel(&Cancel, cx);
5978 assert_eq!(
5979 view.selected_display_ranges(cx),
5980 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
5981 );
5982 });
5983 }
5984
5985 #[gpui::test]
5986 fn test_fold(cx: &mut gpui::MutableAppContext) {
5987 let buffer = MultiBuffer::build_simple(
5988 &"
5989 impl Foo {
5990 // Hello!
5991
5992 fn a() {
5993 1
5994 }
5995
5996 fn b() {
5997 2
5998 }
5999
6000 fn c() {
6001 3
6002 }
6003 }
6004 "
6005 .unindent(),
6006 cx,
6007 );
6008 let settings = EditorSettings::test(&cx);
6009 let (_, view) = cx.add_window(Default::default(), |cx| {
6010 build_editor(buffer.clone(), settings, cx)
6011 });
6012
6013 view.update(cx, |view, cx| {
6014 view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx);
6015 view.fold(&Fold, cx);
6016 assert_eq!(
6017 view.display_text(cx),
6018 "
6019 impl Foo {
6020 // Hello!
6021
6022 fn a() {
6023 1
6024 }
6025
6026 fn b() {…
6027 }
6028
6029 fn c() {…
6030 }
6031 }
6032 "
6033 .unindent(),
6034 );
6035
6036 view.fold(&Fold, cx);
6037 assert_eq!(
6038 view.display_text(cx),
6039 "
6040 impl Foo {…
6041 }
6042 "
6043 .unindent(),
6044 );
6045
6046 view.unfold(&Unfold, cx);
6047 assert_eq!(
6048 view.display_text(cx),
6049 "
6050 impl Foo {
6051 // Hello!
6052
6053 fn a() {
6054 1
6055 }
6056
6057 fn b() {…
6058 }
6059
6060 fn c() {…
6061 }
6062 }
6063 "
6064 .unindent(),
6065 );
6066
6067 view.unfold(&Unfold, cx);
6068 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6069 });
6070 }
6071
6072 #[gpui::test]
6073 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6074 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6075 let settings = EditorSettings::test(&cx);
6076 let (_, view) = cx.add_window(Default::default(), |cx| {
6077 build_editor(buffer.clone(), settings, cx)
6078 });
6079
6080 buffer.update(cx, |buffer, cx| {
6081 buffer.edit(
6082 vec![
6083 Point::new(1, 0)..Point::new(1, 0),
6084 Point::new(1, 1)..Point::new(1, 1),
6085 ],
6086 "\t",
6087 cx,
6088 );
6089 });
6090
6091 view.update(cx, |view, cx| {
6092 assert_eq!(
6093 view.selected_display_ranges(cx),
6094 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6095 );
6096
6097 view.move_down(&MoveDown, cx);
6098 assert_eq!(
6099 view.selected_display_ranges(cx),
6100 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6101 );
6102
6103 view.move_right(&MoveRight, cx);
6104 assert_eq!(
6105 view.selected_display_ranges(cx),
6106 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6107 );
6108
6109 view.move_left(&MoveLeft, cx);
6110 assert_eq!(
6111 view.selected_display_ranges(cx),
6112 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6113 );
6114
6115 view.move_up(&MoveUp, cx);
6116 assert_eq!(
6117 view.selected_display_ranges(cx),
6118 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6119 );
6120
6121 view.move_to_end(&MoveToEnd, cx);
6122 assert_eq!(
6123 view.selected_display_ranges(cx),
6124 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6125 );
6126
6127 view.move_to_beginning(&MoveToBeginning, cx);
6128 assert_eq!(
6129 view.selected_display_ranges(cx),
6130 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6131 );
6132
6133 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx);
6134 view.select_to_beginning(&SelectToBeginning, cx);
6135 assert_eq!(
6136 view.selected_display_ranges(cx),
6137 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6138 );
6139
6140 view.select_to_end(&SelectToEnd, cx);
6141 assert_eq!(
6142 view.selected_display_ranges(cx),
6143 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6144 );
6145 });
6146 }
6147
6148 #[gpui::test]
6149 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6150 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6151 let settings = EditorSettings::test(&cx);
6152 let (_, view) = cx.add_window(Default::default(), |cx| {
6153 build_editor(buffer.clone(), settings, cx)
6154 });
6155
6156 assert_eq!('ⓐ'.len_utf8(), 3);
6157 assert_eq!('α'.len_utf8(), 2);
6158
6159 view.update(cx, |view, cx| {
6160 view.fold_ranges(
6161 vec![
6162 Point::new(0, 6)..Point::new(0, 12),
6163 Point::new(1, 2)..Point::new(1, 4),
6164 Point::new(2, 4)..Point::new(2, 8),
6165 ],
6166 cx,
6167 );
6168 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6169
6170 view.move_right(&MoveRight, cx);
6171 assert_eq!(
6172 view.selected_display_ranges(cx),
6173 &[empty_range(0, "ⓐ".len())]
6174 );
6175 view.move_right(&MoveRight, cx);
6176 assert_eq!(
6177 view.selected_display_ranges(cx),
6178 &[empty_range(0, "ⓐⓑ".len())]
6179 );
6180 view.move_right(&MoveRight, cx);
6181 assert_eq!(
6182 view.selected_display_ranges(cx),
6183 &[empty_range(0, "ⓐⓑ…".len())]
6184 );
6185
6186 view.move_down(&MoveDown, cx);
6187 assert_eq!(
6188 view.selected_display_ranges(cx),
6189 &[empty_range(1, "ab…".len())]
6190 );
6191 view.move_left(&MoveLeft, cx);
6192 assert_eq!(
6193 view.selected_display_ranges(cx),
6194 &[empty_range(1, "ab".len())]
6195 );
6196 view.move_left(&MoveLeft, cx);
6197 assert_eq!(
6198 view.selected_display_ranges(cx),
6199 &[empty_range(1, "a".len())]
6200 );
6201
6202 view.move_down(&MoveDown, cx);
6203 assert_eq!(
6204 view.selected_display_ranges(cx),
6205 &[empty_range(2, "α".len())]
6206 );
6207 view.move_right(&MoveRight, cx);
6208 assert_eq!(
6209 view.selected_display_ranges(cx),
6210 &[empty_range(2, "αβ".len())]
6211 );
6212 view.move_right(&MoveRight, cx);
6213 assert_eq!(
6214 view.selected_display_ranges(cx),
6215 &[empty_range(2, "αβ…".len())]
6216 );
6217 view.move_right(&MoveRight, cx);
6218 assert_eq!(
6219 view.selected_display_ranges(cx),
6220 &[empty_range(2, "αβ…ε".len())]
6221 );
6222
6223 view.move_up(&MoveUp, cx);
6224 assert_eq!(
6225 view.selected_display_ranges(cx),
6226 &[empty_range(1, "ab…e".len())]
6227 );
6228 view.move_up(&MoveUp, cx);
6229 assert_eq!(
6230 view.selected_display_ranges(cx),
6231 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6232 );
6233 view.move_left(&MoveLeft, cx);
6234 assert_eq!(
6235 view.selected_display_ranges(cx),
6236 &[empty_range(0, "ⓐⓑ…".len())]
6237 );
6238 view.move_left(&MoveLeft, cx);
6239 assert_eq!(
6240 view.selected_display_ranges(cx),
6241 &[empty_range(0, "ⓐⓑ".len())]
6242 );
6243 view.move_left(&MoveLeft, cx);
6244 assert_eq!(
6245 view.selected_display_ranges(cx),
6246 &[empty_range(0, "ⓐ".len())]
6247 );
6248 });
6249 }
6250
6251 #[gpui::test]
6252 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6253 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6254 let settings = EditorSettings::test(&cx);
6255 let (_, view) = cx.add_window(Default::default(), |cx| {
6256 build_editor(buffer.clone(), settings, cx)
6257 });
6258 view.update(cx, |view, cx| {
6259 view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx);
6260 view.move_down(&MoveDown, cx);
6261 assert_eq!(
6262 view.selected_display_ranges(cx),
6263 &[empty_range(1, "abcd".len())]
6264 );
6265
6266 view.move_down(&MoveDown, cx);
6267 assert_eq!(
6268 view.selected_display_ranges(cx),
6269 &[empty_range(2, "αβγ".len())]
6270 );
6271
6272 view.move_down(&MoveDown, cx);
6273 assert_eq!(
6274 view.selected_display_ranges(cx),
6275 &[empty_range(3, "abcd".len())]
6276 );
6277
6278 view.move_down(&MoveDown, cx);
6279 assert_eq!(
6280 view.selected_display_ranges(cx),
6281 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6282 );
6283
6284 view.move_up(&MoveUp, cx);
6285 assert_eq!(
6286 view.selected_display_ranges(cx),
6287 &[empty_range(3, "abcd".len())]
6288 );
6289
6290 view.move_up(&MoveUp, cx);
6291 assert_eq!(
6292 view.selected_display_ranges(cx),
6293 &[empty_range(2, "αβγ".len())]
6294 );
6295 });
6296 }
6297
6298 #[gpui::test]
6299 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6300 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6301 let settings = EditorSettings::test(&cx);
6302 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6303 view.update(cx, |view, cx| {
6304 view.select_display_ranges(
6305 &[
6306 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6307 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6308 ],
6309 cx,
6310 );
6311 });
6312
6313 view.update(cx, |view, cx| {
6314 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6315 assert_eq!(
6316 view.selected_display_ranges(cx),
6317 &[
6318 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6319 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6320 ]
6321 );
6322 });
6323
6324 view.update(cx, |view, cx| {
6325 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6326 assert_eq!(
6327 view.selected_display_ranges(cx),
6328 &[
6329 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6330 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6331 ]
6332 );
6333 });
6334
6335 view.update(cx, |view, cx| {
6336 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6337 assert_eq!(
6338 view.selected_display_ranges(cx),
6339 &[
6340 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6341 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6342 ]
6343 );
6344 });
6345
6346 view.update(cx, |view, cx| {
6347 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6348 assert_eq!(
6349 view.selected_display_ranges(cx),
6350 &[
6351 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6352 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6353 ]
6354 );
6355 });
6356
6357 // Moving to the end of line again is a no-op.
6358 view.update(cx, |view, cx| {
6359 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6360 assert_eq!(
6361 view.selected_display_ranges(cx),
6362 &[
6363 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6364 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6365 ]
6366 );
6367 });
6368
6369 view.update(cx, |view, cx| {
6370 view.move_left(&MoveLeft, cx);
6371 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
6372 assert_eq!(
6373 view.selected_display_ranges(cx),
6374 &[
6375 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
6376 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
6377 ]
6378 );
6379 });
6380
6381 view.update(cx, |view, cx| {
6382 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
6383 assert_eq!(
6384 view.selected_display_ranges(cx),
6385 &[
6386 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
6387 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
6388 ]
6389 );
6390 });
6391
6392 view.update(cx, |view, cx| {
6393 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
6394 assert_eq!(
6395 view.selected_display_ranges(cx),
6396 &[
6397 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
6398 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
6399 ]
6400 );
6401 });
6402
6403 view.update(cx, |view, cx| {
6404 view.select_to_end_of_line(&SelectToEndOfLine(true), cx);
6405 assert_eq!(
6406 view.selected_display_ranges(cx),
6407 &[
6408 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6409 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
6410 ]
6411 );
6412 });
6413
6414 view.update(cx, |view, cx| {
6415 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
6416 assert_eq!(view.display_text(cx), "ab\n de");
6417 assert_eq!(
6418 view.selected_display_ranges(cx),
6419 &[
6420 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6421 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6422 ]
6423 );
6424 });
6425
6426 view.update(cx, |view, cx| {
6427 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
6428 assert_eq!(view.display_text(cx), "\n");
6429 assert_eq!(
6430 view.selected_display_ranges(cx),
6431 &[
6432 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6433 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6434 ]
6435 );
6436 });
6437 }
6438
6439 #[gpui::test]
6440 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
6441 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
6442 let settings = EditorSettings::test(&cx);
6443 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6444 view.update(cx, |view, cx| {
6445 view.select_display_ranges(
6446 &[
6447 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
6448 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
6449 ],
6450 cx,
6451 );
6452 });
6453
6454 view.update(cx, |view, cx| {
6455 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6456 assert_eq!(
6457 view.selected_display_ranges(cx),
6458 &[
6459 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
6460 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
6461 ]
6462 );
6463 });
6464
6465 view.update(cx, |view, cx| {
6466 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6467 assert_eq!(
6468 view.selected_display_ranges(cx),
6469 &[
6470 DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
6471 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
6472 ]
6473 );
6474 });
6475
6476 view.update(cx, |view, cx| {
6477 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6478 assert_eq!(
6479 view.selected_display_ranges(cx),
6480 &[
6481 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
6482 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
6483 ]
6484 );
6485 });
6486
6487 view.update(cx, |view, cx| {
6488 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6489 assert_eq!(
6490 view.selected_display_ranges(cx),
6491 &[
6492 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6493 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6494 ]
6495 );
6496 });
6497
6498 view.update(cx, |view, cx| {
6499 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6500 assert_eq!(
6501 view.selected_display_ranges(cx),
6502 &[
6503 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6504 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23),
6505 ]
6506 );
6507 });
6508
6509 view.update(cx, |view, cx| {
6510 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6511 assert_eq!(
6512 view.selected_display_ranges(cx),
6513 &[
6514 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6515 DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
6516 ]
6517 );
6518 });
6519
6520 view.update(cx, |view, cx| {
6521 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6522 assert_eq!(
6523 view.selected_display_ranges(cx),
6524 &[
6525 DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
6526 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6527 ]
6528 );
6529 });
6530
6531 view.update(cx, |view, cx| {
6532 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6533 assert_eq!(
6534 view.selected_display_ranges(cx),
6535 &[
6536 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
6537 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
6538 ]
6539 );
6540 });
6541
6542 view.update(cx, |view, cx| {
6543 view.move_right(&MoveRight, cx);
6544 view.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
6545 assert_eq!(
6546 view.selected_display_ranges(cx),
6547 &[
6548 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
6549 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
6550 ]
6551 );
6552 });
6553
6554 view.update(cx, |view, cx| {
6555 view.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
6556 assert_eq!(
6557 view.selected_display_ranges(cx),
6558 &[
6559 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
6560 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 2),
6561 ]
6562 );
6563 });
6564
6565 view.update(cx, |view, cx| {
6566 view.select_to_next_word_boundary(&SelectToNextWordBoundary, cx);
6567 assert_eq!(
6568 view.selected_display_ranges(cx),
6569 &[
6570 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
6571 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
6572 ]
6573 );
6574 });
6575 }
6576
6577 #[gpui::test]
6578 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
6579 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
6580 let settings = EditorSettings::test(&cx);
6581 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6582
6583 view.update(cx, |view, cx| {
6584 view.set_wrap_width(Some(140.), cx);
6585 assert_eq!(
6586 view.display_text(cx),
6587 "use one::{\n two::three::\n four::five\n};"
6588 );
6589
6590 view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx);
6591
6592 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6593 assert_eq!(
6594 view.selected_display_ranges(cx),
6595 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
6596 );
6597
6598 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6599 assert_eq!(
6600 view.selected_display_ranges(cx),
6601 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
6602 );
6603
6604 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6605 assert_eq!(
6606 view.selected_display_ranges(cx),
6607 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
6608 );
6609
6610 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
6611 assert_eq!(
6612 view.selected_display_ranges(cx),
6613 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
6614 );
6615
6616 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6617 assert_eq!(
6618 view.selected_display_ranges(cx),
6619 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
6620 );
6621
6622 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
6623 assert_eq!(
6624 view.selected_display_ranges(cx),
6625 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
6626 );
6627 });
6628 }
6629
6630 #[gpui::test]
6631 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
6632 let buffer = MultiBuffer::build_simple("one two three four", cx);
6633 let settings = EditorSettings::test(&cx);
6634 let (_, view) = cx.add_window(Default::default(), |cx| {
6635 build_editor(buffer.clone(), settings, cx)
6636 });
6637
6638 view.update(cx, |view, cx| {
6639 view.select_display_ranges(
6640 &[
6641 // an empty selection - the preceding word fragment is deleted
6642 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6643 // characters selected - they are deleted
6644 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
6645 ],
6646 cx,
6647 );
6648 view.delete_to_previous_word_boundary(&DeleteToPreviousWordBoundary, cx);
6649 });
6650
6651 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
6652
6653 view.update(cx, |view, cx| {
6654 view.select_display_ranges(
6655 &[
6656 // an empty selection - the following word fragment is deleted
6657 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6658 // characters selected - they are deleted
6659 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
6660 ],
6661 cx,
6662 );
6663 view.delete_to_next_word_boundary(&DeleteToNextWordBoundary, cx);
6664 });
6665
6666 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
6667 }
6668
6669 #[gpui::test]
6670 fn test_newline(cx: &mut gpui::MutableAppContext) {
6671 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
6672 let settings = EditorSettings::test(&cx);
6673 let (_, view) = cx.add_window(Default::default(), |cx| {
6674 build_editor(buffer.clone(), settings, cx)
6675 });
6676
6677 view.update(cx, |view, cx| {
6678 view.select_display_ranges(
6679 &[
6680 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6681 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6682 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
6683 ],
6684 cx,
6685 );
6686
6687 view.newline(&Newline, cx);
6688 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
6689 });
6690 }
6691
6692 #[gpui::test]
6693 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
6694 let buffer = MultiBuffer::build_simple(
6695 "
6696 a
6697 b(
6698 X
6699 )
6700 c(
6701 X
6702 )
6703 "
6704 .unindent()
6705 .as_str(),
6706 cx,
6707 );
6708
6709 let settings = EditorSettings::test(&cx);
6710 let (_, editor) = cx.add_window(Default::default(), |cx| {
6711 let mut editor = build_editor(buffer.clone(), settings, cx);
6712 editor.select_ranges(
6713 [
6714 Point::new(2, 4)..Point::new(2, 5),
6715 Point::new(5, 4)..Point::new(5, 5),
6716 ],
6717 None,
6718 cx,
6719 );
6720 editor
6721 });
6722
6723 // Edit the buffer directly, deleting ranges surrounding the editor's selections
6724 buffer.update(cx, |buffer, cx| {
6725 buffer.edit(
6726 [
6727 Point::new(1, 2)..Point::new(3, 0),
6728 Point::new(4, 2)..Point::new(6, 0),
6729 ],
6730 "",
6731 cx,
6732 );
6733 assert_eq!(
6734 buffer.read(cx).text(),
6735 "
6736 a
6737 b()
6738 c()
6739 "
6740 .unindent()
6741 );
6742 });
6743
6744 editor.update(cx, |editor, cx| {
6745 assert_eq!(
6746 editor.selected_ranges(cx),
6747 &[
6748 Point::new(1, 2)..Point::new(1, 2),
6749 Point::new(2, 2)..Point::new(2, 2),
6750 ],
6751 );
6752
6753 editor.newline(&Newline, cx);
6754 assert_eq!(
6755 editor.text(cx),
6756 "
6757 a
6758 b(
6759 )
6760 c(
6761 )
6762 "
6763 .unindent()
6764 );
6765
6766 // The selections are moved after the inserted newlines
6767 assert_eq!(
6768 editor.selected_ranges(cx),
6769 &[
6770 Point::new(2, 0)..Point::new(2, 0),
6771 Point::new(4, 0)..Point::new(4, 0),
6772 ],
6773 );
6774 });
6775 }
6776
6777 #[gpui::test]
6778 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
6779 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
6780
6781 let settings = EditorSettings::test(&cx);
6782 let (_, editor) = cx.add_window(Default::default(), |cx| {
6783 let mut editor = build_editor(buffer.clone(), settings, cx);
6784 editor.select_ranges([3..4, 11..12, 19..20], None, cx);
6785 editor
6786 });
6787
6788 // Edit the buffer directly, deleting ranges surrounding the editor's selections
6789 buffer.update(cx, |buffer, cx| {
6790 buffer.edit([2..5, 10..13, 18..21], "", cx);
6791 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
6792 });
6793
6794 editor.update(cx, |editor, cx| {
6795 assert_eq!(editor.selected_ranges(cx), &[2..2, 7..7, 12..12],);
6796
6797 editor.insert("Z", cx);
6798 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
6799
6800 // The selections are moved after the inserted characters
6801 assert_eq!(editor.selected_ranges(cx), &[3..3, 9..9, 15..15],);
6802 });
6803 }
6804
6805 #[gpui::test]
6806 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
6807 let buffer = MultiBuffer::build_simple(" one two\nthree\n four", cx);
6808 let settings = EditorSettings::test(&cx);
6809 let (_, view) = cx.add_window(Default::default(), |cx| {
6810 build_editor(buffer.clone(), settings, cx)
6811 });
6812
6813 view.update(cx, |view, cx| {
6814 // two selections on the same line
6815 view.select_display_ranges(
6816 &[
6817 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 5),
6818 DisplayPoint::new(0, 6)..DisplayPoint::new(0, 9),
6819 ],
6820 cx,
6821 );
6822
6823 // indent from mid-tabstop to full tabstop
6824 view.tab(&Tab, cx);
6825 assert_eq!(view.text(cx), " one two\nthree\n four");
6826 assert_eq!(
6827 view.selected_display_ranges(cx),
6828 &[
6829 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
6830 DisplayPoint::new(0, 8)..DisplayPoint::new(0, 11),
6831 ]
6832 );
6833
6834 // outdent from 1 tabstop to 0 tabstops
6835 view.outdent(&Outdent, cx);
6836 assert_eq!(view.text(cx), "one two\nthree\n four");
6837 assert_eq!(
6838 view.selected_display_ranges(cx),
6839 &[
6840 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 3),
6841 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
6842 ]
6843 );
6844
6845 // select across line ending
6846 view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
6847
6848 // indent and outdent affect only the preceding line
6849 view.tab(&Tab, cx);
6850 assert_eq!(view.text(cx), "one two\n three\n four");
6851 assert_eq!(
6852 view.selected_display_ranges(cx),
6853 &[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
6854 );
6855 view.outdent(&Outdent, cx);
6856 assert_eq!(view.text(cx), "one two\nthree\n four");
6857 assert_eq!(
6858 view.selected_display_ranges(cx),
6859 &[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)]
6860 );
6861
6862 // Ensure that indenting/outdenting works when the cursor is at column 0.
6863 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
6864 view.tab(&Tab, cx);
6865 assert_eq!(view.text(cx), "one two\n three\n four");
6866 assert_eq!(
6867 view.selected_display_ranges(cx),
6868 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6869 );
6870
6871 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
6872 view.outdent(&Outdent, cx);
6873 assert_eq!(view.text(cx), "one two\nthree\n four");
6874 assert_eq!(
6875 view.selected_display_ranges(cx),
6876 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6877 );
6878 });
6879 }
6880
6881 #[gpui::test]
6882 fn test_backspace(cx: &mut gpui::MutableAppContext) {
6883 let buffer =
6884 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
6885 let settings = EditorSettings::test(&cx);
6886 let (_, view) = cx.add_window(Default::default(), |cx| {
6887 build_editor(buffer.clone(), settings, cx)
6888 });
6889
6890 view.update(cx, |view, cx| {
6891 view.select_display_ranges(
6892 &[
6893 // an empty selection - the preceding character is deleted
6894 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6895 // one character selected - it is deleted
6896 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
6897 // a line suffix selected - it is deleted
6898 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
6899 ],
6900 cx,
6901 );
6902 view.backspace(&Backspace, cx);
6903 });
6904
6905 assert_eq!(
6906 buffer.read(cx).read(cx).text(),
6907 "oe two three\nfou five six\nseven ten\n"
6908 );
6909 }
6910
6911 #[gpui::test]
6912 fn test_delete(cx: &mut gpui::MutableAppContext) {
6913 let buffer =
6914 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
6915 let settings = EditorSettings::test(&cx);
6916 let (_, view) = cx.add_window(Default::default(), |cx| {
6917 build_editor(buffer.clone(), settings, cx)
6918 });
6919
6920 view.update(cx, |view, cx| {
6921 view.select_display_ranges(
6922 &[
6923 // an empty selection - the following character is deleted
6924 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6925 // one character selected - it is deleted
6926 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
6927 // a line suffix selected - it is deleted
6928 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
6929 ],
6930 cx,
6931 );
6932 view.delete(&Delete, cx);
6933 });
6934
6935 assert_eq!(
6936 buffer.read(cx).read(cx).text(),
6937 "on two three\nfou five six\nseven ten\n"
6938 );
6939 }
6940
6941 #[gpui::test]
6942 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
6943 let settings = EditorSettings::test(&cx);
6944 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
6945 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6946 view.update(cx, |view, cx| {
6947 view.select_display_ranges(
6948 &[
6949 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6950 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
6951 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
6952 ],
6953 cx,
6954 );
6955 view.delete_line(&DeleteLine, cx);
6956 assert_eq!(view.display_text(cx), "ghi");
6957 assert_eq!(
6958 view.selected_display_ranges(cx),
6959 vec![
6960 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6961 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
6962 ]
6963 );
6964 });
6965
6966 let settings = EditorSettings::test(&cx);
6967 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
6968 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6969 view.update(cx, |view, cx| {
6970 view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx);
6971 view.delete_line(&DeleteLine, cx);
6972 assert_eq!(view.display_text(cx), "ghi\n");
6973 assert_eq!(
6974 view.selected_display_ranges(cx),
6975 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
6976 );
6977 });
6978 }
6979
6980 #[gpui::test]
6981 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
6982 let settings = EditorSettings::test(&cx);
6983 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
6984 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
6985 view.update(cx, |view, cx| {
6986 view.select_display_ranges(
6987 &[
6988 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
6989 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
6990 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6991 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
6992 ],
6993 cx,
6994 );
6995 view.duplicate_line(&DuplicateLine, cx);
6996 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
6997 assert_eq!(
6998 view.selected_display_ranges(cx),
6999 vec![
7000 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7001 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7002 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7003 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7004 ]
7005 );
7006 });
7007
7008 let settings = EditorSettings::test(&cx);
7009 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7010 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7011 view.update(cx, |view, cx| {
7012 view.select_display_ranges(
7013 &[
7014 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7015 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7016 ],
7017 cx,
7018 );
7019 view.duplicate_line(&DuplicateLine, cx);
7020 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7021 assert_eq!(
7022 view.selected_display_ranges(cx),
7023 vec![
7024 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7025 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7026 ]
7027 );
7028 });
7029 }
7030
7031 #[gpui::test]
7032 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7033 let settings = EditorSettings::test(&cx);
7034 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7035 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7036 view.update(cx, |view, cx| {
7037 view.fold_ranges(
7038 vec![
7039 Point::new(0, 2)..Point::new(1, 2),
7040 Point::new(2, 3)..Point::new(4, 1),
7041 Point::new(7, 0)..Point::new(8, 4),
7042 ],
7043 cx,
7044 );
7045 view.select_display_ranges(
7046 &[
7047 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7048 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7049 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7050 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7051 ],
7052 cx,
7053 );
7054 assert_eq!(
7055 view.display_text(cx),
7056 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7057 );
7058
7059 view.move_line_up(&MoveLineUp, cx);
7060 assert_eq!(
7061 view.display_text(cx),
7062 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7063 );
7064 assert_eq!(
7065 view.selected_display_ranges(cx),
7066 vec![
7067 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7068 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7069 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7070 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7071 ]
7072 );
7073 });
7074
7075 view.update(cx, |view, cx| {
7076 view.move_line_down(&MoveLineDown, cx);
7077 assert_eq!(
7078 view.display_text(cx),
7079 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7080 );
7081 assert_eq!(
7082 view.selected_display_ranges(cx),
7083 vec![
7084 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7085 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7086 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7087 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7088 ]
7089 );
7090 });
7091
7092 view.update(cx, |view, cx| {
7093 view.move_line_down(&MoveLineDown, cx);
7094 assert_eq!(
7095 view.display_text(cx),
7096 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7097 );
7098 assert_eq!(
7099 view.selected_display_ranges(cx),
7100 vec![
7101 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7102 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7103 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7104 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7105 ]
7106 );
7107 });
7108
7109 view.update(cx, |view, cx| {
7110 view.move_line_up(&MoveLineUp, cx);
7111 assert_eq!(
7112 view.display_text(cx),
7113 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7114 );
7115 assert_eq!(
7116 view.selected_display_ranges(cx),
7117 vec![
7118 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7119 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7120 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7121 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7122 ]
7123 );
7124 });
7125 }
7126
7127 #[gpui::test]
7128 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7129 let settings = EditorSettings::test(&cx);
7130 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7131 let snapshot = buffer.read(cx).snapshot(cx);
7132 let (_, editor) =
7133 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7134 editor.update(cx, |editor, cx| {
7135 editor.insert_blocks(
7136 [BlockProperties {
7137 position: snapshot.anchor_after(Point::new(2, 0)),
7138 disposition: BlockDisposition::Below,
7139 height: 1,
7140 render: Arc::new(|_| Empty::new().boxed()),
7141 }],
7142 cx,
7143 );
7144 editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx);
7145 editor.move_line_down(&MoveLineDown, cx);
7146 });
7147 }
7148
7149 #[gpui::test]
7150 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
7151 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
7152 let settings = EditorSettings::test(&cx);
7153 let view = cx
7154 .add_window(Default::default(), |cx| {
7155 build_editor(buffer.clone(), settings, cx)
7156 })
7157 .1;
7158
7159 // Cut with three selections. Clipboard text is divided into three slices.
7160 view.update(cx, |view, cx| {
7161 view.select_ranges(vec![0..7, 11..17, 22..27], None, cx);
7162 view.cut(&Cut, cx);
7163 assert_eq!(view.display_text(cx), "two four six ");
7164 });
7165
7166 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
7167 view.update(cx, |view, cx| {
7168 view.select_ranges(vec![4..4, 9..9, 13..13], None, cx);
7169 view.paste(&Paste, cx);
7170 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
7171 assert_eq!(
7172 view.selected_display_ranges(cx),
7173 &[
7174 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7175 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
7176 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
7177 ]
7178 );
7179 });
7180
7181 // Paste again but with only two cursors. Since the number of cursors doesn't
7182 // match the number of slices in the clipboard, the entire clipboard text
7183 // is pasted at each cursor.
7184 view.update(cx, |view, cx| {
7185 view.select_ranges(vec![0..0, 31..31], None, cx);
7186 view.handle_input(&Input("( ".into()), cx);
7187 view.paste(&Paste, cx);
7188 view.handle_input(&Input(") ".into()), cx);
7189 assert_eq!(
7190 view.display_text(cx),
7191 "( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
7192 );
7193 });
7194
7195 view.update(cx, |view, cx| {
7196 view.select_ranges(vec![0..0], None, cx);
7197 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
7198 assert_eq!(
7199 view.display_text(cx),
7200 "123\n4567\n89\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
7201 );
7202 });
7203
7204 // Cut with three selections, one of which is full-line.
7205 view.update(cx, |view, cx| {
7206 view.select_display_ranges(
7207 &[
7208 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
7209 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7210 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
7211 ],
7212 cx,
7213 );
7214 view.cut(&Cut, cx);
7215 assert_eq!(
7216 view.display_text(cx),
7217 "13\n9\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
7218 );
7219 });
7220
7221 // Paste with three selections, noticing how the copied selection that was full-line
7222 // gets inserted before the second cursor.
7223 view.update(cx, |view, cx| {
7224 view.select_display_ranges(
7225 &[
7226 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7227 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7228 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
7229 ],
7230 cx,
7231 );
7232 view.paste(&Paste, cx);
7233 assert_eq!(
7234 view.display_text(cx),
7235 "123\n4567\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
7236 );
7237 assert_eq!(
7238 view.selected_display_ranges(cx),
7239 &[
7240 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7241 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7242 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
7243 ]
7244 );
7245 });
7246
7247 // Copy with a single cursor only, which writes the whole line into the clipboard.
7248 view.update(cx, |view, cx| {
7249 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx);
7250 view.copy(&Copy, cx);
7251 });
7252
7253 // Paste with three selections, noticing how the copied full-line selection is inserted
7254 // before the empty selections but replaces the selection that is non-empty.
7255 view.update(cx, |view, cx| {
7256 view.select_display_ranges(
7257 &[
7258 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7259 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
7260 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7261 ],
7262 cx,
7263 );
7264 view.paste(&Paste, cx);
7265 assert_eq!(
7266 view.display_text(cx),
7267 "123\n123\n123\n67\n123\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
7268 );
7269 assert_eq!(
7270 view.selected_display_ranges(cx),
7271 &[
7272 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7273 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7274 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
7275 ]
7276 );
7277 });
7278 }
7279
7280 #[gpui::test]
7281 fn test_select_all(cx: &mut gpui::MutableAppContext) {
7282 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
7283 let settings = EditorSettings::test(&cx);
7284 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7285 view.update(cx, |view, cx| {
7286 view.select_all(&SelectAll, cx);
7287 assert_eq!(
7288 view.selected_display_ranges(cx),
7289 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
7290 );
7291 });
7292 }
7293
7294 #[gpui::test]
7295 fn test_select_line(cx: &mut gpui::MutableAppContext) {
7296 let settings = EditorSettings::test(&cx);
7297 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
7298 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7299 view.update(cx, |view, cx| {
7300 view.select_display_ranges(
7301 &[
7302 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7303 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7304 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7305 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
7306 ],
7307 cx,
7308 );
7309 view.select_line(&SelectLine, cx);
7310 assert_eq!(
7311 view.selected_display_ranges(cx),
7312 vec![
7313 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
7314 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
7315 ]
7316 );
7317 });
7318
7319 view.update(cx, |view, cx| {
7320 view.select_line(&SelectLine, cx);
7321 assert_eq!(
7322 view.selected_display_ranges(cx),
7323 vec![
7324 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
7325 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
7326 ]
7327 );
7328 });
7329
7330 view.update(cx, |view, cx| {
7331 view.select_line(&SelectLine, cx);
7332 assert_eq!(
7333 view.selected_display_ranges(cx),
7334 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
7335 );
7336 });
7337 }
7338
7339 #[gpui::test]
7340 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
7341 let settings = EditorSettings::test(&cx);
7342 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
7343 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7344 view.update(cx, |view, cx| {
7345 view.fold_ranges(
7346 vec![
7347 Point::new(0, 2)..Point::new(1, 2),
7348 Point::new(2, 3)..Point::new(4, 1),
7349 Point::new(7, 0)..Point::new(8, 4),
7350 ],
7351 cx,
7352 );
7353 view.select_display_ranges(
7354 &[
7355 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7356 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7357 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7358 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
7359 ],
7360 cx,
7361 );
7362 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
7363 });
7364
7365 view.update(cx, |view, cx| {
7366 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
7367 assert_eq!(
7368 view.display_text(cx),
7369 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
7370 );
7371 assert_eq!(
7372 view.selected_display_ranges(cx),
7373 [
7374 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7375 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7376 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
7377 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
7378 ]
7379 );
7380 });
7381
7382 view.update(cx, |view, cx| {
7383 view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx);
7384 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
7385 assert_eq!(
7386 view.display_text(cx),
7387 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
7388 );
7389 assert_eq!(
7390 view.selected_display_ranges(cx),
7391 [
7392 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
7393 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7394 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7395 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
7396 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
7397 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
7398 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
7399 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
7400 ]
7401 );
7402 });
7403 }
7404
7405 #[gpui::test]
7406 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
7407 let settings = EditorSettings::test(&cx);
7408 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
7409 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
7410
7411 view.update(cx, |view, cx| {
7412 view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx);
7413 });
7414 view.update(cx, |view, cx| {
7415 view.add_selection_above(&AddSelectionAbove, cx);
7416 assert_eq!(
7417 view.selected_display_ranges(cx),
7418 vec![
7419 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7420 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
7421 ]
7422 );
7423 });
7424
7425 view.update(cx, |view, cx| {
7426 view.add_selection_above(&AddSelectionAbove, cx);
7427 assert_eq!(
7428 view.selected_display_ranges(cx),
7429 vec![
7430 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7431 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
7432 ]
7433 );
7434 });
7435
7436 view.update(cx, |view, cx| {
7437 view.add_selection_below(&AddSelectionBelow, cx);
7438 assert_eq!(
7439 view.selected_display_ranges(cx),
7440 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
7441 );
7442 });
7443
7444 view.update(cx, |view, cx| {
7445 view.add_selection_below(&AddSelectionBelow, cx);
7446 assert_eq!(
7447 view.selected_display_ranges(cx),
7448 vec![
7449 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
7450 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
7451 ]
7452 );
7453 });
7454
7455 view.update(cx, |view, cx| {
7456 view.add_selection_below(&AddSelectionBelow, cx);
7457 assert_eq!(
7458 view.selected_display_ranges(cx),
7459 vec![
7460 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
7461 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
7462 ]
7463 );
7464 });
7465
7466 view.update(cx, |view, cx| {
7467 view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx);
7468 });
7469 view.update(cx, |view, cx| {
7470 view.add_selection_below(&AddSelectionBelow, cx);
7471 assert_eq!(
7472 view.selected_display_ranges(cx),
7473 vec![
7474 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7475 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
7476 ]
7477 );
7478 });
7479
7480 view.update(cx, |view, cx| {
7481 view.add_selection_below(&AddSelectionBelow, cx);
7482 assert_eq!(
7483 view.selected_display_ranges(cx),
7484 vec![
7485 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7486 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
7487 ]
7488 );
7489 });
7490
7491 view.update(cx, |view, cx| {
7492 view.add_selection_above(&AddSelectionAbove, cx);
7493 assert_eq!(
7494 view.selected_display_ranges(cx),
7495 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
7496 );
7497 });
7498
7499 view.update(cx, |view, cx| {
7500 view.add_selection_above(&AddSelectionAbove, cx);
7501 assert_eq!(
7502 view.selected_display_ranges(cx),
7503 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
7504 );
7505 });
7506
7507 view.update(cx, |view, cx| {
7508 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx);
7509 view.add_selection_below(&AddSelectionBelow, cx);
7510 assert_eq!(
7511 view.selected_display_ranges(cx),
7512 vec![
7513 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7514 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
7515 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
7516 ]
7517 );
7518 });
7519
7520 view.update(cx, |view, cx| {
7521 view.add_selection_below(&AddSelectionBelow, cx);
7522 assert_eq!(
7523 view.selected_display_ranges(cx),
7524 vec![
7525 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7526 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
7527 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
7528 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
7529 ]
7530 );
7531 });
7532
7533 view.update(cx, |view, cx| {
7534 view.add_selection_above(&AddSelectionAbove, cx);
7535 assert_eq!(
7536 view.selected_display_ranges(cx),
7537 vec![
7538 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
7539 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
7540 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
7541 ]
7542 );
7543 });
7544
7545 view.update(cx, |view, cx| {
7546 view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx);
7547 });
7548 view.update(cx, |view, cx| {
7549 view.add_selection_above(&AddSelectionAbove, cx);
7550 assert_eq!(
7551 view.selected_display_ranges(cx),
7552 vec![
7553 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
7554 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
7555 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
7556 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
7557 ]
7558 );
7559 });
7560
7561 view.update(cx, |view, cx| {
7562 view.add_selection_below(&AddSelectionBelow, cx);
7563 assert_eq!(
7564 view.selected_display_ranges(cx),
7565 vec![
7566 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
7567 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
7568 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
7569 ]
7570 );
7571 });
7572 }
7573
7574 #[gpui::test]
7575 async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
7576 let settings = cx.read(EditorSettings::test);
7577 let language = Arc::new(Language::new(
7578 LanguageConfig::default(),
7579 Some(tree_sitter_rust::language()),
7580 ));
7581
7582 let text = r#"
7583 use mod1::mod2::{mod3, mod4};
7584
7585 fn fn_1(param1: bool, param2: &str) {
7586 let var1 = "text";
7587 }
7588 "#
7589 .unindent();
7590
7591 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
7592 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7593 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
7594 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7595 .await;
7596
7597 view.update(&mut cx, |view, cx| {
7598 view.select_display_ranges(
7599 &[
7600 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
7601 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
7602 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
7603 ],
7604 cx,
7605 );
7606 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
7607 });
7608 assert_eq!(
7609 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7610 &[
7611 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
7612 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
7613 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
7614 ]
7615 );
7616
7617 view.update(&mut cx, |view, cx| {
7618 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
7619 });
7620 assert_eq!(
7621 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7622 &[
7623 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
7624 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
7625 ]
7626 );
7627
7628 view.update(&mut cx, |view, cx| {
7629 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
7630 });
7631 assert_eq!(
7632 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7633 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
7634 );
7635
7636 // Trying to expand the selected syntax node one more time has no effect.
7637 view.update(&mut cx, |view, cx| {
7638 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
7639 });
7640 assert_eq!(
7641 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7642 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
7643 );
7644
7645 view.update(&mut cx, |view, cx| {
7646 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
7647 });
7648 assert_eq!(
7649 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7650 &[
7651 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
7652 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
7653 ]
7654 );
7655
7656 view.update(&mut cx, |view, cx| {
7657 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
7658 });
7659 assert_eq!(
7660 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7661 &[
7662 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
7663 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
7664 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
7665 ]
7666 );
7667
7668 view.update(&mut cx, |view, cx| {
7669 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
7670 });
7671 assert_eq!(
7672 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7673 &[
7674 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
7675 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
7676 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
7677 ]
7678 );
7679
7680 // Trying to shrink the selected syntax node one more time has no effect.
7681 view.update(&mut cx, |view, cx| {
7682 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
7683 });
7684 assert_eq!(
7685 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7686 &[
7687 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
7688 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
7689 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
7690 ]
7691 );
7692
7693 // Ensure that we keep expanding the selection if the larger selection starts or ends within
7694 // a fold.
7695 view.update(&mut cx, |view, cx| {
7696 view.fold_ranges(
7697 vec![
7698 Point::new(0, 21)..Point::new(0, 24),
7699 Point::new(3, 20)..Point::new(3, 22),
7700 ],
7701 cx,
7702 );
7703 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
7704 });
7705 assert_eq!(
7706 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
7707 &[
7708 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
7709 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
7710 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
7711 ]
7712 );
7713 }
7714
7715 #[gpui::test]
7716 async fn test_autoindent_selections(mut cx: gpui::TestAppContext) {
7717 let settings = cx.read(EditorSettings::test);
7718 let language = Arc::new(
7719 Language::new(
7720 LanguageConfig {
7721 brackets: vec![
7722 BracketPair {
7723 start: "{".to_string(),
7724 end: "}".to_string(),
7725 close: false,
7726 newline: true,
7727 },
7728 BracketPair {
7729 start: "(".to_string(),
7730 end: ")".to_string(),
7731 close: false,
7732 newline: true,
7733 },
7734 ],
7735 ..Default::default()
7736 },
7737 Some(tree_sitter_rust::language()),
7738 )
7739 .with_indents_query(
7740 r#"
7741 (_ "(" ")" @end) @indent
7742 (_ "{" "}" @end) @indent
7743 "#,
7744 )
7745 .unwrap(),
7746 );
7747
7748 let text = "fn a() {}";
7749
7750 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
7751 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7752 let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
7753 editor
7754 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
7755 .await;
7756
7757 editor.update(&mut cx, |editor, cx| {
7758 editor.select_ranges([5..5, 8..8, 9..9], None, cx);
7759 editor.newline(&Newline, cx);
7760 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
7761 assert_eq!(
7762 editor.selected_ranges(cx),
7763 &[
7764 Point::new(1, 4)..Point::new(1, 4),
7765 Point::new(3, 4)..Point::new(3, 4),
7766 Point::new(5, 0)..Point::new(5, 0)
7767 ]
7768 );
7769 });
7770 }
7771
7772 #[gpui::test]
7773 async fn test_autoclose_pairs(mut cx: gpui::TestAppContext) {
7774 let settings = cx.read(EditorSettings::test);
7775 let language = Arc::new(Language::new(
7776 LanguageConfig {
7777 brackets: vec![
7778 BracketPair {
7779 start: "{".to_string(),
7780 end: "}".to_string(),
7781 close: true,
7782 newline: true,
7783 },
7784 BracketPair {
7785 start: "/*".to_string(),
7786 end: " */".to_string(),
7787 close: true,
7788 newline: true,
7789 },
7790 ],
7791 ..Default::default()
7792 },
7793 Some(tree_sitter_rust::language()),
7794 ));
7795
7796 let text = r#"
7797 a
7798
7799 /
7800
7801 "#
7802 .unindent();
7803
7804 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
7805 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7806 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
7807 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7808 .await;
7809
7810 view.update(&mut cx, |view, cx| {
7811 view.select_display_ranges(
7812 &[
7813 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7814 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7815 ],
7816 cx,
7817 );
7818 view.handle_input(&Input("{".to_string()), cx);
7819 view.handle_input(&Input("{".to_string()), cx);
7820 view.handle_input(&Input("{".to_string()), cx);
7821 assert_eq!(
7822 view.text(cx),
7823 "
7824 {{{}}}
7825 {{{}}}
7826 /
7827
7828 "
7829 .unindent()
7830 );
7831
7832 view.move_right(&MoveRight, cx);
7833 view.handle_input(&Input("}".to_string()), cx);
7834 view.handle_input(&Input("}".to_string()), cx);
7835 view.handle_input(&Input("}".to_string()), cx);
7836 assert_eq!(
7837 view.text(cx),
7838 "
7839 {{{}}}}
7840 {{{}}}}
7841 /
7842
7843 "
7844 .unindent()
7845 );
7846
7847 view.undo(&Undo, cx);
7848 view.handle_input(&Input("/".to_string()), cx);
7849 view.handle_input(&Input("*".to_string()), cx);
7850 assert_eq!(
7851 view.text(cx),
7852 "
7853 /* */
7854 /* */
7855 /
7856
7857 "
7858 .unindent()
7859 );
7860
7861 view.undo(&Undo, cx);
7862 view.select_display_ranges(
7863 &[
7864 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7865 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7866 ],
7867 cx,
7868 );
7869 view.handle_input(&Input("*".to_string()), cx);
7870 assert_eq!(
7871 view.text(cx),
7872 "
7873 a
7874
7875 /*
7876 *
7877 "
7878 .unindent()
7879 );
7880 });
7881 }
7882
7883 #[gpui::test]
7884 async fn test_snippets(mut cx: gpui::TestAppContext) {
7885 let settings = cx.read(EditorSettings::test);
7886
7887 let text = "
7888 a. b
7889 a. b
7890 a. b
7891 "
7892 .unindent();
7893 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
7894 let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
7895
7896 editor.update(&mut cx, |editor, cx| {
7897 let buffer = &editor.snapshot(cx).buffer_snapshot;
7898 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
7899 let insertion_ranges = [
7900 Point::new(0, 2).to_offset(buffer)..Point::new(0, 2).to_offset(buffer),
7901 Point::new(1, 2).to_offset(buffer)..Point::new(1, 2).to_offset(buffer),
7902 Point::new(2, 2).to_offset(buffer)..Point::new(2, 2).to_offset(buffer),
7903 ];
7904
7905 editor
7906 .insert_snippet(&insertion_ranges, snippet, cx)
7907 .unwrap();
7908 assert_eq!(
7909 editor.text(cx),
7910 "
7911 a.f(one, two, three) b
7912 a.f(one, two, three) b
7913 a.f(one, two, three) b
7914 "
7915 .unindent()
7916 );
7917 assert_eq!(
7918 editor.selected_ranges::<Point>(cx),
7919 &[
7920 Point::new(0, 4)..Point::new(0, 7),
7921 Point::new(0, 14)..Point::new(0, 19),
7922 Point::new(1, 4)..Point::new(1, 7),
7923 Point::new(1, 14)..Point::new(1, 19),
7924 Point::new(2, 4)..Point::new(2, 7),
7925 Point::new(2, 14)..Point::new(2, 19),
7926 ]
7927 );
7928
7929 // Can't move earlier than the first tab stop
7930 editor.move_to_prev_snippet_tabstop(cx);
7931 assert_eq!(
7932 editor.selected_ranges::<Point>(cx),
7933 &[
7934 Point::new(0, 4)..Point::new(0, 7),
7935 Point::new(0, 14)..Point::new(0, 19),
7936 Point::new(1, 4)..Point::new(1, 7),
7937 Point::new(1, 14)..Point::new(1, 19),
7938 Point::new(2, 4)..Point::new(2, 7),
7939 Point::new(2, 14)..Point::new(2, 19),
7940 ]
7941 );
7942
7943 assert!(editor.move_to_next_snippet_tabstop(cx));
7944 assert_eq!(
7945 editor.selected_ranges::<Point>(cx),
7946 &[
7947 Point::new(0, 9)..Point::new(0, 12),
7948 Point::new(1, 9)..Point::new(1, 12),
7949 Point::new(2, 9)..Point::new(2, 12)
7950 ]
7951 );
7952
7953 editor.move_to_prev_snippet_tabstop(cx);
7954 assert_eq!(
7955 editor.selected_ranges::<Point>(cx),
7956 &[
7957 Point::new(0, 4)..Point::new(0, 7),
7958 Point::new(0, 14)..Point::new(0, 19),
7959 Point::new(1, 4)..Point::new(1, 7),
7960 Point::new(1, 14)..Point::new(1, 19),
7961 Point::new(2, 4)..Point::new(2, 7),
7962 Point::new(2, 14)..Point::new(2, 19),
7963 ]
7964 );
7965
7966 assert!(editor.move_to_next_snippet_tabstop(cx));
7967 assert!(editor.move_to_next_snippet_tabstop(cx));
7968 assert_eq!(
7969 editor.selected_ranges::<Point>(cx),
7970 &[
7971 Point::new(0, 20)..Point::new(0, 20),
7972 Point::new(1, 20)..Point::new(1, 20),
7973 Point::new(2, 20)..Point::new(2, 20)
7974 ]
7975 );
7976
7977 // As soon as the last tab stop is reached, snippet state is gone
7978 editor.move_to_prev_snippet_tabstop(cx);
7979 assert_eq!(
7980 editor.selected_ranges::<Point>(cx),
7981 &[
7982 Point::new(0, 20)..Point::new(0, 20),
7983 Point::new(1, 20)..Point::new(1, 20),
7984 Point::new(2, 20)..Point::new(2, 20)
7985 ]
7986 );
7987 });
7988 }
7989
7990 #[gpui::test]
7991 async fn test_completion(mut cx: gpui::TestAppContext) {
7992 let settings = cx.read(EditorSettings::test);
7993 let (language_server, mut fake) = lsp::LanguageServer::fake_with_capabilities(
7994 lsp::ServerCapabilities {
7995 completion_provider: Some(lsp::CompletionOptions {
7996 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7997 ..Default::default()
7998 }),
7999 ..Default::default()
8000 },
8001 cx.background(),
8002 );
8003
8004 let text = "
8005 one
8006 two
8007 three
8008 "
8009 .unindent();
8010
8011 let fs = FakeFs::new(cx.background().clone());
8012 fs.insert_file("/file", text).await;
8013
8014 let project = Project::test(fs, &mut cx);
8015
8016 let (worktree, relative_path) = project
8017 .update(&mut cx, |project, cx| {
8018 project.find_or_create_local_worktree("/file", false, cx)
8019 })
8020 .await
8021 .unwrap();
8022 let project_path = ProjectPath {
8023 worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
8024 path: relative_path.into(),
8025 };
8026 let buffer = project
8027 .update(&mut cx, |project, cx| project.open_buffer(project_path, cx))
8028 .await
8029 .unwrap();
8030 buffer.update(&mut cx, |buffer, cx| {
8031 buffer.set_language_server(Some(language_server), cx);
8032 });
8033
8034 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8035 buffer.next_notification(&cx).await;
8036
8037 let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
8038
8039 editor.update(&mut cx, |editor, cx| {
8040 editor.project = Some(project);
8041 editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx);
8042 editor.handle_input(&Input(".".to_string()), cx);
8043 });
8044
8045 handle_completion_request(
8046 &mut fake,
8047 "/file",
8048 Point::new(0, 4),
8049 vec![
8050 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
8051 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
8052 ],
8053 )
8054 .await;
8055 editor
8056 .condition(&cx, |editor, _| editor.context_menu_visible())
8057 .await;
8058
8059 let apply_additional_edits = editor.update(&mut cx, |editor, cx| {
8060 editor.move_down(&MoveDown, cx);
8061 let apply_additional_edits = editor
8062 .confirm_completion(&ConfirmCompletion(None), cx)
8063 .unwrap();
8064 assert_eq!(
8065 editor.text(cx),
8066 "
8067 one.second_completion
8068 two
8069 three
8070 "
8071 .unindent()
8072 );
8073 apply_additional_edits
8074 });
8075
8076 handle_resolve_completion_request(
8077 &mut fake,
8078 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
8079 )
8080 .await;
8081 apply_additional_edits.await.unwrap();
8082 assert_eq!(
8083 editor.read_with(&cx, |editor, cx| editor.text(cx)),
8084 "
8085 one.second_completion
8086 two
8087 three
8088 additional edit
8089 "
8090 .unindent()
8091 );
8092
8093 editor.update(&mut cx, |editor, cx| {
8094 editor.select_ranges(
8095 [
8096 Point::new(1, 3)..Point::new(1, 3),
8097 Point::new(2, 5)..Point::new(2, 5),
8098 ],
8099 None,
8100 cx,
8101 );
8102
8103 editor.handle_input(&Input(" ".to_string()), cx);
8104 assert!(editor.context_menu.is_none());
8105 editor.handle_input(&Input("s".to_string()), cx);
8106 assert!(editor.context_menu.is_none());
8107 });
8108
8109 handle_completion_request(
8110 &mut fake,
8111 "/file",
8112 Point::new(2, 7),
8113 vec![
8114 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
8115 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
8116 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
8117 ],
8118 )
8119 .await;
8120 editor
8121 .condition(&cx, |editor, _| editor.context_menu_visible())
8122 .await;
8123
8124 editor.update(&mut cx, |editor, cx| {
8125 editor.handle_input(&Input("i".to_string()), cx);
8126 });
8127
8128 handle_completion_request(
8129 &mut fake,
8130 "/file",
8131 Point::new(2, 8),
8132 vec![
8133 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
8134 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
8135 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
8136 ],
8137 )
8138 .await;
8139 editor
8140 .condition(&cx, |editor, _| editor.context_menu_visible())
8141 .await;
8142
8143 let apply_additional_edits = editor.update(&mut cx, |editor, cx| {
8144 let apply_additional_edits = editor
8145 .confirm_completion(&ConfirmCompletion(None), cx)
8146 .unwrap();
8147 assert_eq!(
8148 editor.text(cx),
8149 "
8150 one.second_completion
8151 two sixth_completion
8152 three sixth_completion
8153 additional edit
8154 "
8155 .unindent()
8156 );
8157 apply_additional_edits
8158 });
8159 handle_resolve_completion_request(&mut fake, None).await;
8160 apply_additional_edits.await.unwrap();
8161
8162 async fn handle_completion_request(
8163 fake: &mut FakeLanguageServer,
8164 path: &'static str,
8165 position: Point,
8166 completions: Vec<(Range<Point>, &'static str)>,
8167 ) {
8168 fake.handle_request::<lsp::request::Completion, _>(move |params| {
8169 assert_eq!(
8170 params.text_document_position.text_document.uri,
8171 lsp::Url::from_file_path(path).unwrap()
8172 );
8173 assert_eq!(
8174 params.text_document_position.position,
8175 lsp::Position::new(position.row, position.column)
8176 );
8177 Some(lsp::CompletionResponse::Array(
8178 completions
8179 .iter()
8180 .map(|(range, new_text)| lsp::CompletionItem {
8181 label: new_text.to_string(),
8182 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8183 range: lsp::Range::new(
8184 lsp::Position::new(range.start.row, range.start.column),
8185 lsp::Position::new(range.start.row, range.start.column),
8186 ),
8187 new_text: new_text.to_string(),
8188 })),
8189 ..Default::default()
8190 })
8191 .collect(),
8192 ))
8193 })
8194 .next()
8195 .await;
8196 }
8197
8198 async fn handle_resolve_completion_request(
8199 fake: &mut FakeLanguageServer,
8200 edit: Option<(Range<Point>, &'static str)>,
8201 ) {
8202 fake.handle_request::<lsp::request::ResolveCompletionItem, _>(move |_| {
8203 lsp::CompletionItem {
8204 additional_text_edits: edit.clone().map(|(range, new_text)| {
8205 vec![lsp::TextEdit::new(
8206 lsp::Range::new(
8207 lsp::Position::new(range.start.row, range.start.column),
8208 lsp::Position::new(range.end.row, range.end.column),
8209 ),
8210 new_text.to_string(),
8211 )]
8212 }),
8213 ..Default::default()
8214 }
8215 })
8216 .next()
8217 .await;
8218 }
8219 }
8220
8221 #[gpui::test]
8222 async fn test_toggle_comment(mut cx: gpui::TestAppContext) {
8223 let settings = cx.read(EditorSettings::test);
8224 let language = Arc::new(Language::new(
8225 LanguageConfig {
8226 line_comment: Some("// ".to_string()),
8227 ..Default::default()
8228 },
8229 Some(tree_sitter_rust::language()),
8230 ));
8231
8232 let text = "
8233 fn a() {
8234 //b();
8235 // c();
8236 // d();
8237 }
8238 "
8239 .unindent();
8240
8241 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8242 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8243 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
8244
8245 view.update(&mut cx, |editor, cx| {
8246 // If multiple selections intersect a line, the line is only
8247 // toggled once.
8248 editor.select_display_ranges(
8249 &[
8250 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
8251 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
8252 ],
8253 cx,
8254 );
8255 editor.toggle_comments(&ToggleComments, cx);
8256 assert_eq!(
8257 editor.text(cx),
8258 "
8259 fn a() {
8260 b();
8261 c();
8262 d();
8263 }
8264 "
8265 .unindent()
8266 );
8267
8268 // The comment prefix is inserted at the same column for every line
8269 // in a selection.
8270 editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx);
8271 editor.toggle_comments(&ToggleComments, cx);
8272 assert_eq!(
8273 editor.text(cx),
8274 "
8275 fn a() {
8276 // b();
8277 // c();
8278 // d();
8279 }
8280 "
8281 .unindent()
8282 );
8283
8284 // If a selection ends at the beginning of a line, that line is not toggled.
8285 editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx);
8286 editor.toggle_comments(&ToggleComments, cx);
8287 assert_eq!(
8288 editor.text(cx),
8289 "
8290 fn a() {
8291 // b();
8292 c();
8293 // d();
8294 }
8295 "
8296 .unindent()
8297 );
8298 });
8299 }
8300
8301 #[gpui::test]
8302 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
8303 let settings = EditorSettings::test(cx);
8304 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
8305 let multibuffer = cx.add_model(|cx| {
8306 let mut multibuffer = MultiBuffer::new(0);
8307 multibuffer.push_excerpts(
8308 buffer.clone(),
8309 [
8310 Point::new(0, 0)..Point::new(0, 4),
8311 Point::new(1, 0)..Point::new(1, 4),
8312 ],
8313 cx,
8314 );
8315 multibuffer
8316 });
8317
8318 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
8319
8320 let (_, view) = cx.add_window(Default::default(), |cx| {
8321 build_editor(multibuffer, settings, cx)
8322 });
8323 view.update(cx, |view, cx| {
8324 assert_eq!(view.text(cx), "aaaa\nbbbb");
8325 view.select_ranges(
8326 [
8327 Point::new(0, 0)..Point::new(0, 0),
8328 Point::new(1, 0)..Point::new(1, 0),
8329 ],
8330 None,
8331 cx,
8332 );
8333
8334 view.handle_input(&Input("X".to_string()), cx);
8335 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
8336 assert_eq!(
8337 view.selected_ranges(cx),
8338 [
8339 Point::new(0, 1)..Point::new(0, 1),
8340 Point::new(1, 1)..Point::new(1, 1),
8341 ]
8342 )
8343 });
8344 }
8345
8346 #[gpui::test]
8347 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
8348 let settings = EditorSettings::test(cx);
8349 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
8350 let multibuffer = cx.add_model(|cx| {
8351 let mut multibuffer = MultiBuffer::new(0);
8352 multibuffer.push_excerpts(
8353 buffer,
8354 [
8355 Point::new(0, 0)..Point::new(1, 4),
8356 Point::new(1, 0)..Point::new(2, 4),
8357 ],
8358 cx,
8359 );
8360 multibuffer
8361 });
8362
8363 assert_eq!(
8364 multibuffer.read(cx).read(cx).text(),
8365 "aaaa\nbbbb\nbbbb\ncccc"
8366 );
8367
8368 let (_, view) = cx.add_window(Default::default(), |cx| {
8369 build_editor(multibuffer, settings, cx)
8370 });
8371 view.update(cx, |view, cx| {
8372 view.select_ranges(
8373 [
8374 Point::new(1, 1)..Point::new(1, 1),
8375 Point::new(2, 3)..Point::new(2, 3),
8376 ],
8377 None,
8378 cx,
8379 );
8380
8381 view.handle_input(&Input("X".to_string()), cx);
8382 assert_eq!(view.text(cx), "aaaa\nbXbbXb\nbXbbXb\ncccc");
8383 assert_eq!(
8384 view.selected_ranges(cx),
8385 [
8386 Point::new(1, 2)..Point::new(1, 2),
8387 Point::new(2, 5)..Point::new(2, 5),
8388 ]
8389 );
8390
8391 view.newline(&Newline, cx);
8392 assert_eq!(view.text(cx), "aaaa\nbX\nbbX\nb\nbX\nbbX\nb\ncccc");
8393 assert_eq!(
8394 view.selected_ranges(cx),
8395 [
8396 Point::new(2, 0)..Point::new(2, 0),
8397 Point::new(6, 0)..Point::new(6, 0),
8398 ]
8399 );
8400 });
8401 }
8402
8403 #[gpui::test]
8404 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
8405 let settings = EditorSettings::test(cx);
8406 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
8407 let mut excerpt1_id = None;
8408 let multibuffer = cx.add_model(|cx| {
8409 let mut multibuffer = MultiBuffer::new(0);
8410 excerpt1_id = multibuffer
8411 .push_excerpts(
8412 buffer.clone(),
8413 [
8414 Point::new(0, 0)..Point::new(1, 4),
8415 Point::new(1, 0)..Point::new(2, 4),
8416 ],
8417 cx,
8418 )
8419 .into_iter()
8420 .next();
8421 multibuffer
8422 });
8423 assert_eq!(
8424 multibuffer.read(cx).read(cx).text(),
8425 "aaaa\nbbbb\nbbbb\ncccc"
8426 );
8427 let (_, editor) = cx.add_window(Default::default(), |cx| {
8428 let mut editor = build_editor(multibuffer.clone(), settings, cx);
8429 editor.select_ranges(
8430 [
8431 Point::new(1, 3)..Point::new(1, 3),
8432 Point::new(2, 1)..Point::new(2, 1),
8433 ],
8434 None,
8435 cx,
8436 );
8437 editor
8438 });
8439
8440 // Refreshing selections is a no-op when excerpts haven't changed.
8441 editor.update(cx, |editor, cx| {
8442 editor.refresh_selections(cx);
8443 assert_eq!(
8444 editor.selected_ranges(cx),
8445 [
8446 Point::new(1, 3)..Point::new(1, 3),
8447 Point::new(2, 1)..Point::new(2, 1),
8448 ]
8449 );
8450 });
8451
8452 multibuffer.update(cx, |multibuffer, cx| {
8453 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
8454 });
8455 editor.update(cx, |editor, cx| {
8456 // Removing an excerpt causes the first selection to become degenerate.
8457 assert_eq!(
8458 editor.selected_ranges(cx),
8459 [
8460 Point::new(0, 0)..Point::new(0, 0),
8461 Point::new(0, 1)..Point::new(0, 1)
8462 ]
8463 );
8464
8465 // Refreshing selections will relocate the first selection to the original buffer
8466 // location.
8467 editor.refresh_selections(cx);
8468 assert_eq!(
8469 editor.selected_ranges(cx),
8470 [
8471 Point::new(0, 1)..Point::new(0, 1),
8472 Point::new(0, 3)..Point::new(0, 3)
8473 ]
8474 );
8475 });
8476 }
8477
8478 #[gpui::test]
8479 async fn test_extra_newline_insertion(mut cx: gpui::TestAppContext) {
8480 let settings = cx.read(EditorSettings::test);
8481 let language = Arc::new(Language::new(
8482 LanguageConfig {
8483 brackets: vec![
8484 BracketPair {
8485 start: "{".to_string(),
8486 end: "}".to_string(),
8487 close: true,
8488 newline: true,
8489 },
8490 BracketPair {
8491 start: "/* ".to_string(),
8492 end: " */".to_string(),
8493 close: true,
8494 newline: true,
8495 },
8496 ],
8497 ..Default::default()
8498 },
8499 Some(tree_sitter_rust::language()),
8500 ));
8501
8502 let text = concat!(
8503 "{ }\n", // Suppress rustfmt
8504 " x\n", //
8505 " /* */\n", //
8506 "x\n", //
8507 "{{} }\n", //
8508 );
8509
8510 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8511 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8512 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
8513 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8514 .await;
8515
8516 view.update(&mut cx, |view, cx| {
8517 view.select_display_ranges(
8518 &[
8519 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
8520 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8521 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8522 ],
8523 cx,
8524 );
8525 view.newline(&Newline, cx);
8526
8527 assert_eq!(
8528 view.buffer().read(cx).read(cx).text(),
8529 concat!(
8530 "{ \n", // Suppress rustfmt
8531 "\n", //
8532 "}\n", //
8533 " x\n", //
8534 " /* \n", //
8535 " \n", //
8536 " */\n", //
8537 "x\n", //
8538 "{{} \n", //
8539 "}\n", //
8540 )
8541 );
8542 });
8543 }
8544
8545 #[gpui::test]
8546 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
8547 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
8548 let settings = EditorSettings::test(&cx);
8549 let (_, editor) = cx.add_window(Default::default(), |cx| {
8550 build_editor(buffer.clone(), settings, cx)
8551 });
8552
8553 editor.update(cx, |editor, cx| {
8554 struct Type1;
8555 struct Type2;
8556
8557 let buffer = buffer.read(cx).snapshot(cx);
8558
8559 let anchor_range = |range: Range<Point>| {
8560 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
8561 };
8562
8563 editor.highlight_ranges::<Type1>(
8564 vec![
8565 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
8566 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
8567 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
8568 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
8569 ],
8570 Color::red(),
8571 cx,
8572 );
8573 editor.highlight_ranges::<Type2>(
8574 vec![
8575 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
8576 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
8577 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
8578 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
8579 ],
8580 Color::green(),
8581 cx,
8582 );
8583
8584 let snapshot = editor.snapshot(cx);
8585 let mut highlighted_ranges = editor.highlighted_ranges_in_range(
8586 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
8587 &snapshot,
8588 );
8589 // Enforce a consistent ordering based on color without relying on the ordering of the
8590 // highlight's `TypeId` which is non-deterministic.
8591 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
8592 assert_eq!(
8593 highlighted_ranges,
8594 &[
8595 (
8596 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
8597 Color::green(),
8598 ),
8599 (
8600 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
8601 Color::green(),
8602 ),
8603 (
8604 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
8605 Color::red(),
8606 ),
8607 (
8608 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
8609 Color::red(),
8610 ),
8611 ]
8612 );
8613 assert_eq!(
8614 editor.highlighted_ranges_in_range(
8615 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
8616 &snapshot,
8617 ),
8618 &[(
8619 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
8620 Color::red(),
8621 )]
8622 );
8623 });
8624 }
8625
8626 #[test]
8627 fn test_combine_syntax_and_fuzzy_match_highlights() {
8628 let string = "abcdefghijklmnop";
8629 let default = HighlightStyle::default();
8630 let syntax_ranges = [
8631 (
8632 0..3,
8633 HighlightStyle {
8634 color: Color::red(),
8635 ..default
8636 },
8637 ),
8638 (
8639 4..8,
8640 HighlightStyle {
8641 color: Color::green(),
8642 ..default
8643 },
8644 ),
8645 ];
8646 let match_indices = [4, 6, 7, 8];
8647 assert_eq!(
8648 combine_syntax_and_fuzzy_match_highlights(
8649 &string,
8650 default,
8651 syntax_ranges.into_iter(),
8652 &match_indices,
8653 ),
8654 &[
8655 (
8656 0..3,
8657 HighlightStyle {
8658 color: Color::red(),
8659 ..default
8660 },
8661 ),
8662 (
8663 4..5,
8664 HighlightStyle {
8665 color: Color::green(),
8666 font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
8667 ..default
8668 },
8669 ),
8670 (
8671 5..6,
8672 HighlightStyle {
8673 color: Color::green(),
8674 ..default
8675 },
8676 ),
8677 (
8678 6..8,
8679 HighlightStyle {
8680 color: Color::green(),
8681 font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
8682 ..default
8683 },
8684 ),
8685 (
8686 8..9,
8687 HighlightStyle {
8688 font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
8689 ..default
8690 },
8691 ),
8692 ]
8693 );
8694 }
8695
8696 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8697 let point = DisplayPoint::new(row as u32, column as u32);
8698 point..point
8699 }
8700
8701 fn build_editor(
8702 buffer: ModelHandle<MultiBuffer>,
8703 settings: EditorSettings,
8704 cx: &mut ViewContext<Editor>,
8705 ) -> Editor {
8706 Editor::for_buffer(buffer, Arc::new(move |_| settings.clone()), None, cx)
8707 }
8708}
8709
8710trait RangeExt<T> {
8711 fn sorted(&self) -> Range<T>;
8712 fn to_inclusive(&self) -> RangeInclusive<T>;
8713}
8714
8715impl<T: Ord + Clone> RangeExt<T> for Range<T> {
8716 fn sorted(&self) -> Self {
8717 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
8718 }
8719
8720 fn to_inclusive(&self) -> RangeInclusive<T> {
8721 self.start.clone()..=self.end.clone()
8722 }
8723}