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