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