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