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