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