1// use super::*;
2// use crate::{
3// scroll::scroll_amount::ScrollAmount,
4// test::{
5// assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
6// editor_test_context::EditorTestContext, select_ranges,
7// },
8// JoinLines,
9// };
10// use drag_and_drop::DragAndDrop;
11// use futures::StreamExt;
12// use gpui::{
13// executor::Deterministic,
14// geometry::{rect::RectF, vector::vec2f},
15// platform::{WindowBounds, WindowOptions},
16// serde_json::{self, json},
17// TestAppContext,
18// };
19// use indoc::indoc;
20// use language::{
21// language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
22// BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
23// Override, Point,
24// };
25// use parking_lot::Mutex;
26// use project::project_settings::{LspSettings, ProjectSettings};
27// use project::FakeFs;
28// use std::sync::atomic;
29// use std::sync::atomic::AtomicUsize;
30// use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
31// use unindent::Unindent;
32// use util::{
33// assert_set_eq,
34// test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
35// };
36// use workspace::{
37// item::{FollowableItem, Item, ItemHandle},
38// NavigationEntry, ViewId,
39// };
40
41// #[gpui::test]
42// fn test_edit_events(cx: &mut TestAppContext) {
43// init_test(cx, |_| {});
44
45// let buffer = cx.add_model(|cx| {
46// let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "123456");
47// buffer.set_group_interval(Duration::from_secs(1));
48// buffer
49// });
50
51// let events = Rc::new(RefCell::new(Vec::new()));
52// let editor1 = cx
53// .add_window({
54// let events = events.clone();
55// |cx| {
56// cx.subscribe(&cx.handle(), move |_, _, event, _| {
57// if matches!(
58// event,
59// Event::Edited | Event::BufferEdited | Event::DirtyChanged
60// ) {
61// events.borrow_mut().push(("editor1", event.clone()));
62// }
63// })
64// .detach();
65// Editor::for_buffer(buffer.clone(), None, cx)
66// }
67// })
68// .root(cx);
69// let editor2 = cx
70// .add_window({
71// let events = events.clone();
72// |cx| {
73// cx.subscribe(&cx.handle(), move |_, _, event, _| {
74// if matches!(
75// event,
76// Event::Edited | Event::BufferEdited | Event::DirtyChanged
77// ) {
78// events.borrow_mut().push(("editor2", event.clone()));
79// }
80// })
81// .detach();
82// Editor::for_buffer(buffer.clone(), None, cx)
83// }
84// })
85// .root(cx);
86// assert_eq!(mem::take(&mut *events.borrow_mut()), []);
87
88// // Mutating editor 1 will emit an `Edited` event only for that editor.
89// editor1.update(cx, |editor, cx| editor.insert("X", cx));
90// assert_eq!(
91// mem::take(&mut *events.borrow_mut()),
92// [
93// ("editor1", Event::Edited),
94// ("editor1", Event::BufferEdited),
95// ("editor2", Event::BufferEdited),
96// ("editor1", Event::DirtyChanged),
97// ("editor2", Event::DirtyChanged)
98// ]
99// );
100
101// // Mutating editor 2 will emit an `Edited` event only for that editor.
102// editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
103// assert_eq!(
104// mem::take(&mut *events.borrow_mut()),
105// [
106// ("editor2", Event::Edited),
107// ("editor1", Event::BufferEdited),
108// ("editor2", Event::BufferEdited),
109// ]
110// );
111
112// // Undoing on editor 1 will emit an `Edited` event only for that editor.
113// editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
114// assert_eq!(
115// mem::take(&mut *events.borrow_mut()),
116// [
117// ("editor1", Event::Edited),
118// ("editor1", Event::BufferEdited),
119// ("editor2", Event::BufferEdited),
120// ("editor1", Event::DirtyChanged),
121// ("editor2", Event::DirtyChanged),
122// ]
123// );
124
125// // Redoing on editor 1 will emit an `Edited` event only for that editor.
126// editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
127// assert_eq!(
128// mem::take(&mut *events.borrow_mut()),
129// [
130// ("editor1", Event::Edited),
131// ("editor1", Event::BufferEdited),
132// ("editor2", Event::BufferEdited),
133// ("editor1", Event::DirtyChanged),
134// ("editor2", Event::DirtyChanged),
135// ]
136// );
137
138// // Undoing on editor 2 will emit an `Edited` event only for that editor.
139// editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
140// assert_eq!(
141// mem::take(&mut *events.borrow_mut()),
142// [
143// ("editor2", Event::Edited),
144// ("editor1", Event::BufferEdited),
145// ("editor2", Event::BufferEdited),
146// ("editor1", Event::DirtyChanged),
147// ("editor2", Event::DirtyChanged),
148// ]
149// );
150
151// // Redoing on editor 2 will emit an `Edited` event only for that editor.
152// editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
153// assert_eq!(
154// mem::take(&mut *events.borrow_mut()),
155// [
156// ("editor2", Event::Edited),
157// ("editor1", Event::BufferEdited),
158// ("editor2", Event::BufferEdited),
159// ("editor1", Event::DirtyChanged),
160// ("editor2", Event::DirtyChanged),
161// ]
162// );
163
164// // No event is emitted when the mutation is a no-op.
165// editor2.update(cx, |editor, cx| {
166// editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
167
168// editor.backspace(&Backspace, cx);
169// });
170// assert_eq!(mem::take(&mut *events.borrow_mut()), []);
171// }
172
173// #[gpui::test]
174// fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
175// init_test(cx, |_| {});
176
177// let mut now = Instant::now();
178// let buffer = cx.add_model(|cx| language::Buffer::new(0, cx.model_id() as u64, "123456"));
179// let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
180// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
181// let editor = cx
182// .add_window(|cx| build_editor(buffer.clone(), cx))
183// .root(cx);
184
185// editor.update(cx, |editor, cx| {
186// editor.start_transaction_at(now, cx);
187// editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
188
189// editor.insert("cd", cx);
190// editor.end_transaction_at(now, cx);
191// assert_eq!(editor.text(cx), "12cd56");
192// assert_eq!(editor.selections.ranges(cx), vec![4..4]);
193
194// editor.start_transaction_at(now, cx);
195// editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
196// editor.insert("e", cx);
197// editor.end_transaction_at(now, cx);
198// assert_eq!(editor.text(cx), "12cde6");
199// assert_eq!(editor.selections.ranges(cx), vec![5..5]);
200
201// now += group_interval + Duration::from_millis(1);
202// editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
203
204// // Simulate an edit in another editor
205// buffer.update(cx, |buffer, cx| {
206// buffer.start_transaction_at(now, cx);
207// buffer.edit([(0..1, "a")], None, cx);
208// buffer.edit([(1..1, "b")], None, cx);
209// buffer.end_transaction_at(now, cx);
210// });
211
212// assert_eq!(editor.text(cx), "ab2cde6");
213// assert_eq!(editor.selections.ranges(cx), vec![3..3]);
214
215// // Last transaction happened past the group interval in a different editor.
216// // Undo it individually and don't restore selections.
217// editor.undo(&Undo, cx);
218// assert_eq!(editor.text(cx), "12cde6");
219// assert_eq!(editor.selections.ranges(cx), vec![2..2]);
220
221// // First two transactions happened within the group interval in this editor.
222// // Undo them together and restore selections.
223// editor.undo(&Undo, cx);
224// editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
225// assert_eq!(editor.text(cx), "123456");
226// assert_eq!(editor.selections.ranges(cx), vec![0..0]);
227
228// // Redo the first two transactions together.
229// editor.redo(&Redo, cx);
230// assert_eq!(editor.text(cx), "12cde6");
231// assert_eq!(editor.selections.ranges(cx), vec![5..5]);
232
233// // Redo the last transaction on its own.
234// editor.redo(&Redo, cx);
235// assert_eq!(editor.text(cx), "ab2cde6");
236// assert_eq!(editor.selections.ranges(cx), vec![6..6]);
237
238// // Test empty transactions.
239// editor.start_transaction_at(now, cx);
240// editor.end_transaction_at(now, cx);
241// editor.undo(&Undo, cx);
242// assert_eq!(editor.text(cx), "12cde6");
243// });
244// }
245
246// #[gpui::test]
247// fn test_ime_composition(cx: &mut TestAppContext) {
248// init_test(cx, |_| {});
249
250// let buffer = cx.add_model(|cx| {
251// let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "abcde");
252// // Ensure automatic grouping doesn't occur.
253// buffer.set_group_interval(Duration::ZERO);
254// buffer
255// });
256
257// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
258// cx.add_window(|cx| {
259// let mut editor = build_editor(buffer.clone(), cx);
260
261// // Start a new IME composition.
262// editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
263// editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
264// editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
265// assert_eq!(editor.text(cx), "äbcde");
266// assert_eq!(
267// editor.marked_text_ranges(cx),
268// Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
269// );
270
271// // Finalize IME composition.
272// editor.replace_text_in_range(None, "ā", cx);
273// assert_eq!(editor.text(cx), "ābcde");
274// assert_eq!(editor.marked_text_ranges(cx), None);
275
276// // IME composition edits are grouped and are undone/redone at once.
277// editor.undo(&Default::default(), cx);
278// assert_eq!(editor.text(cx), "abcde");
279// assert_eq!(editor.marked_text_ranges(cx), None);
280// editor.redo(&Default::default(), cx);
281// assert_eq!(editor.text(cx), "ābcde");
282// assert_eq!(editor.marked_text_ranges(cx), None);
283
284// // Start a new IME composition.
285// editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
286// assert_eq!(
287// editor.marked_text_ranges(cx),
288// Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
289// );
290
291// // Undoing during an IME composition cancels it.
292// editor.undo(&Default::default(), cx);
293// assert_eq!(editor.text(cx), "ābcde");
294// assert_eq!(editor.marked_text_ranges(cx), None);
295
296// // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
297// editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
298// assert_eq!(editor.text(cx), "ābcdè");
299// assert_eq!(
300// editor.marked_text_ranges(cx),
301// Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
302// );
303
304// // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
305// editor.replace_text_in_range(Some(4..999), "ę", cx);
306// assert_eq!(editor.text(cx), "ābcdę");
307// assert_eq!(editor.marked_text_ranges(cx), None);
308
309// // Start a new IME composition with multiple cursors.
310// editor.change_selections(None, cx, |s| {
311// s.select_ranges([
312// OffsetUtf16(1)..OffsetUtf16(1),
313// OffsetUtf16(3)..OffsetUtf16(3),
314// OffsetUtf16(5)..OffsetUtf16(5),
315// ])
316// });
317// editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
318// assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
319// assert_eq!(
320// editor.marked_text_ranges(cx),
321// Some(vec![
322// OffsetUtf16(0)..OffsetUtf16(3),
323// OffsetUtf16(4)..OffsetUtf16(7),
324// OffsetUtf16(8)..OffsetUtf16(11)
325// ])
326// );
327
328// // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
329// editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
330// assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
331// assert_eq!(
332// editor.marked_text_ranges(cx),
333// Some(vec![
334// OffsetUtf16(1)..OffsetUtf16(2),
335// OffsetUtf16(5)..OffsetUtf16(6),
336// OffsetUtf16(9)..OffsetUtf16(10)
337// ])
338// );
339
340// // Finalize IME composition with multiple cursors.
341// editor.replace_text_in_range(Some(9..10), "2", cx);
342// assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
343// assert_eq!(editor.marked_text_ranges(cx), None);
344
345// editor
346// });
347// }
348
349// #[gpui::test]
350// fn test_selection_with_mouse(cx: &mut TestAppContext) {
351// init_test(cx, |_| {});
352
353// let editor = cx
354// .add_window(|cx| {
355// let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
356// build_editor(buffer, cx)
357// })
358// .root(cx);
359// editor.update(cx, |view, cx| {
360// view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
361// });
362// assert_eq!(
363// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
364// [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
365// );
366
367// editor.update(cx, |view, cx| {
368// view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
369// });
370
371// assert_eq!(
372// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
373// [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
374// );
375
376// editor.update(cx, |view, cx| {
377// view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
378// });
379
380// assert_eq!(
381// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
382// [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
383// );
384
385// editor.update(cx, |view, cx| {
386// view.end_selection(cx);
387// view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
388// });
389
390// assert_eq!(
391// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
392// [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
393// );
394
395// editor.update(cx, |view, cx| {
396// view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
397// view.update_selection(DisplayPoint::new(0, 0), 0, Point<Pixels>::zero(), cx);
398// });
399
400// assert_eq!(
401// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
402// [
403// DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
404// DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
405// ]
406// );
407
408// editor.update(cx, |view, cx| {
409// view.end_selection(cx);
410// });
411
412// assert_eq!(
413// editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
414// [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
415// );
416// }
417
418// #[gpui::test]
419// fn test_canceling_pending_selection(cx: &mut TestAppContext) {
420// init_test(cx, |_| {});
421
422// let view = cx
423// .add_window(|cx| {
424// let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
425// build_editor(buffer, cx)
426// })
427// .root(cx);
428
429// view.update(cx, |view, cx| {
430// view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
431// assert_eq!(
432// view.selections.display_ranges(cx),
433// [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
434// );
435// });
436
437// view.update(cx, |view, cx| {
438// view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
439// assert_eq!(
440// view.selections.display_ranges(cx),
441// [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
442// );
443// });
444
445// view.update(cx, |view, cx| {
446// view.cancel(&Cancel, cx);
447// view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
448// assert_eq!(
449// view.selections.display_ranges(cx),
450// [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
451// );
452// });
453// }
454
455// #[gpui::test]
456// fn test_clone(cx: &mut TestAppContext) {
457// init_test(cx, |_| {});
458
459// let (text, selection_ranges) = marked_text_ranges(
460// indoc! {"
461// one
462// two
463// threeˇ
464// four
465// fiveˇ
466// "},
467// true,
468// );
469
470// let editor = cx
471// .add_window(|cx| {
472// let buffer = MultiBuffer::build_simple(&text, cx);
473// build_editor(buffer, cx)
474// })
475// .root(cx);
476
477// editor.update(cx, |editor, cx| {
478// editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
479// editor.fold_ranges(
480// [
481// Point::new(1, 0)..Point::new(2, 0),
482// Point::new(3, 0)..Point::new(4, 0),
483// ],
484// true,
485// cx,
486// );
487// });
488
489// let cloned_editor = editor
490// .update(cx, |editor, cx| {
491// cx.add_window(Default::default(), |cx| editor.clone(cx))
492// })
493// .root(cx);
494
495// let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
496// let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
497
498// assert_eq!(
499// cloned_editor.update(cx, |e, cx| e.display_text(cx)),
500// editor.update(cx, |e, cx| e.display_text(cx))
501// );
502// assert_eq!(
503// cloned_snapshot
504// .folds_in_range(0..text.len())
505// .collect::<Vec<_>>(),
506// snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
507// );
508// assert_set_eq!(
509// cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
510// editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
511// );
512// assert_set_eq!(
513// cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
514// editor.update(cx, |e, cx| e.selections.display_ranges(cx))
515// );
516// }
517
518// #[gpui::test]
519// async fn test_navigation_history(cx: &mut TestAppContext) {
520// init_test(cx, |_| {});
521
522// cx.set_global(DragAndDrop::<Workspace>::default());
523// use workspace::item::Item;
524
525// let fs = FakeFs::new(cx.background());
526// let project = Project::test(fs, [], cx).await;
527// let window = cx.add_window(|cx| Workspace::test_new(project, cx));
528// let workspace = window.root(cx);
529// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
530// window.add_view(cx, |cx| {
531// let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
532// let mut editor = build_editor(buffer.clone(), cx);
533// let handle = cx.handle();
534// editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
535
536// fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
537// editor.nav_history.as_mut().unwrap().pop_backward(cx)
538// }
539
540// // Move the cursor a small distance.
541// // Nothing is added to the navigation history.
542// editor.change_selections(None, cx, |s| {
543// s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
544// });
545// editor.change_selections(None, cx, |s| {
546// s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
547// });
548// assert!(pop_history(&mut editor, cx).is_none());
549
550// // Move the cursor a large distance.
551// // The history can jump back to the previous position.
552// editor.change_selections(None, cx, |s| {
553// s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
554// });
555// let nav_entry = pop_history(&mut editor, cx).unwrap();
556// editor.navigate(nav_entry.data.unwrap(), cx);
557// assert_eq!(nav_entry.item.id(), cx.view_id());
558// assert_eq!(
559// editor.selections.display_ranges(cx),
560// &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
561// );
562// assert!(pop_history(&mut editor, cx).is_none());
563
564// // Move the cursor a small distance via the mouse.
565// // Nothing is added to the navigation history.
566// editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
567// editor.end_selection(cx);
568// assert_eq!(
569// editor.selections.display_ranges(cx),
570// &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
571// );
572// assert!(pop_history(&mut editor, cx).is_none());
573
574// // Move the cursor a large distance via the mouse.
575// // The history can jump back to the previous position.
576// editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
577// editor.end_selection(cx);
578// assert_eq!(
579// editor.selections.display_ranges(cx),
580// &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
581// );
582// let nav_entry = pop_history(&mut editor, cx).unwrap();
583// editor.navigate(nav_entry.data.unwrap(), cx);
584// assert_eq!(nav_entry.item.id(), cx.view_id());
585// assert_eq!(
586// editor.selections.display_ranges(cx),
587// &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
588// );
589// assert!(pop_history(&mut editor, cx).is_none());
590
591// // Set scroll position to check later
592// editor.set_scroll_position(Point<Pixels>::new(5.5, 5.5), cx);
593// let original_scroll_position = editor.scroll_manager.anchor();
594
595// // Jump to the end of the document and adjust scroll
596// editor.move_to_end(&MoveToEnd, cx);
597// editor.set_scroll_position(Point<Pixels>::new(-2.5, -0.5), cx);
598// assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
599
600// let nav_entry = pop_history(&mut editor, cx).unwrap();
601// editor.navigate(nav_entry.data.unwrap(), cx);
602// assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
603
604// // Ensure we don't panic when navigation data contains invalid anchors *and* points.
605// let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
606// invalid_anchor.text_anchor.buffer_id = Some(999);
607// let invalid_point = Point::new(9999, 0);
608// editor.navigate(
609// Box::new(NavigationData {
610// cursor_anchor: invalid_anchor,
611// cursor_position: invalid_point,
612// scroll_anchor: ScrollAnchor {
613// anchor: invalid_anchor,
614// offset: Default::default(),
615// },
616// scroll_top_row: invalid_point.row,
617// }),
618// cx,
619// );
620// assert_eq!(
621// editor.selections.display_ranges(cx),
622// &[editor.max_point(cx)..editor.max_point(cx)]
623// );
624// assert_eq!(
625// editor.scroll_position(cx),
626// vec2f(0., editor.max_point(cx).row() as f32)
627// );
628
629// editor
630// });
631// }
632
633// #[gpui::test]
634// fn test_cancel(cx: &mut TestAppContext) {
635// init_test(cx, |_| {});
636
637// let view = cx
638// .add_window(|cx| {
639// let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
640// build_editor(buffer, cx)
641// })
642// .root(cx);
643
644// view.update(cx, |view, cx| {
645// view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
646// view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
647// view.end_selection(cx);
648
649// view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
650// view.update_selection(DisplayPoint::new(0, 3), 0, Point<Pixels>::zero(), cx);
651// view.end_selection(cx);
652// assert_eq!(
653// view.selections.display_ranges(cx),
654// [
655// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
656// DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
657// ]
658// );
659// });
660
661// view.update(cx, |view, cx| {
662// view.cancel(&Cancel, cx);
663// assert_eq!(
664// view.selections.display_ranges(cx),
665// [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
666// );
667// });
668
669// view.update(cx, |view, cx| {
670// view.cancel(&Cancel, cx);
671// assert_eq!(
672// view.selections.display_ranges(cx),
673// [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
674// );
675// });
676// }
677
678// #[gpui::test]
679// fn test_fold_action(cx: &mut TestAppContext) {
680// init_test(cx, |_| {});
681
682// let view = cx
683// .add_window(|cx| {
684// let buffer = MultiBuffer::build_simple(
685// &"
686// impl Foo {
687// // Hello!
688
689// fn a() {
690// 1
691// }
692
693// fn b() {
694// 2
695// }
696
697// fn c() {
698// 3
699// }
700// }
701// "
702// .unindent(),
703// cx,
704// );
705// build_editor(buffer.clone(), cx)
706// })
707// .root(cx);
708
709// view.update(cx, |view, cx| {
710// view.change_selections(None, cx, |s| {
711// s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
712// });
713// view.fold(&Fold, cx);
714// assert_eq!(
715// view.display_text(cx),
716// "
717// impl Foo {
718// // Hello!
719
720// fn a() {
721// 1
722// }
723
724// fn b() {⋯
725// }
726
727// fn c() {⋯
728// }
729// }
730// "
731// .unindent(),
732// );
733
734// view.fold(&Fold, cx);
735// assert_eq!(
736// view.display_text(cx),
737// "
738// impl Foo {⋯
739// }
740// "
741// .unindent(),
742// );
743
744// view.unfold_lines(&UnfoldLines, cx);
745// assert_eq!(
746// view.display_text(cx),
747// "
748// impl Foo {
749// // Hello!
750
751// fn a() {
752// 1
753// }
754
755// fn b() {⋯
756// }
757
758// fn c() {⋯
759// }
760// }
761// "
762// .unindent(),
763// );
764
765// view.unfold_lines(&UnfoldLines, cx);
766// assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
767// });
768// }
769
770// #[gpui::test]
771// fn test_move_cursor(cx: &mut TestAppContext) {
772// init_test(cx, |_| {});
773
774// let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
775// let view = cx
776// .add_window(|cx| build_editor(buffer.clone(), cx))
777// .root(cx);
778
779// buffer.update(cx, |buffer, cx| {
780// buffer.edit(
781// vec![
782// (Point::new(1, 0)..Point::new(1, 0), "\t"),
783// (Point::new(1, 1)..Point::new(1, 1), "\t"),
784// ],
785// None,
786// cx,
787// );
788// });
789// view.update(cx, |view, cx| {
790// assert_eq!(
791// view.selections.display_ranges(cx),
792// &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
793// );
794
795// view.move_down(&MoveDown, cx);
796// assert_eq!(
797// view.selections.display_ranges(cx),
798// &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
799// );
800
801// view.move_right(&MoveRight, cx);
802// assert_eq!(
803// view.selections.display_ranges(cx),
804// &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
805// );
806
807// view.move_left(&MoveLeft, cx);
808// assert_eq!(
809// view.selections.display_ranges(cx),
810// &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
811// );
812
813// view.move_up(&MoveUp, cx);
814// assert_eq!(
815// view.selections.display_ranges(cx),
816// &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
817// );
818
819// view.move_to_end(&MoveToEnd, cx);
820// assert_eq!(
821// view.selections.display_ranges(cx),
822// &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
823// );
824
825// view.move_to_beginning(&MoveToBeginning, cx);
826// assert_eq!(
827// view.selections.display_ranges(cx),
828// &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
829// );
830
831// view.change_selections(None, cx, |s| {
832// s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
833// });
834// view.select_to_beginning(&SelectToBeginning, cx);
835// assert_eq!(
836// view.selections.display_ranges(cx),
837// &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
838// );
839
840// view.select_to_end(&SelectToEnd, cx);
841// assert_eq!(
842// view.selections.display_ranges(cx),
843// &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
844// );
845// });
846// }
847
848// #[gpui::test]
849// fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
850// init_test(cx, |_| {});
851
852// let view = cx
853// .add_window(|cx| {
854// let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
855// build_editor(buffer.clone(), cx)
856// })
857// .root(cx);
858
859// assert_eq!('ⓐ'.len_utf8(), 3);
860// assert_eq!('α'.len_utf8(), 2);
861
862// view.update(cx, |view, cx| {
863// view.fold_ranges(
864// vec![
865// Point::new(0, 6)..Point::new(0, 12),
866// Point::new(1, 2)..Point::new(1, 4),
867// Point::new(2, 4)..Point::new(2, 8),
868// ],
869// true,
870// cx,
871// );
872// assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
873
874// view.move_right(&MoveRight, cx);
875// assert_eq!(
876// view.selections.display_ranges(cx),
877// &[empty_range(0, "ⓐ".len())]
878// );
879// view.move_right(&MoveRight, cx);
880// assert_eq!(
881// view.selections.display_ranges(cx),
882// &[empty_range(0, "ⓐⓑ".len())]
883// );
884// view.move_right(&MoveRight, cx);
885// assert_eq!(
886// view.selections.display_ranges(cx),
887// &[empty_range(0, "ⓐⓑ⋯".len())]
888// );
889
890// view.move_down(&MoveDown, cx);
891// assert_eq!(
892// view.selections.display_ranges(cx),
893// &[empty_range(1, "ab⋯e".len())]
894// );
895// view.move_left(&MoveLeft, cx);
896// assert_eq!(
897// view.selections.display_ranges(cx),
898// &[empty_range(1, "ab⋯".len())]
899// );
900// view.move_left(&MoveLeft, cx);
901// assert_eq!(
902// view.selections.display_ranges(cx),
903// &[empty_range(1, "ab".len())]
904// );
905// view.move_left(&MoveLeft, cx);
906// assert_eq!(
907// view.selections.display_ranges(cx),
908// &[empty_range(1, "a".len())]
909// );
910
911// view.move_down(&MoveDown, cx);
912// assert_eq!(
913// view.selections.display_ranges(cx),
914// &[empty_range(2, "α".len())]
915// );
916// view.move_right(&MoveRight, cx);
917// assert_eq!(
918// view.selections.display_ranges(cx),
919// &[empty_range(2, "αβ".len())]
920// );
921// view.move_right(&MoveRight, cx);
922// assert_eq!(
923// view.selections.display_ranges(cx),
924// &[empty_range(2, "αβ⋯".len())]
925// );
926// view.move_right(&MoveRight, cx);
927// assert_eq!(
928// view.selections.display_ranges(cx),
929// &[empty_range(2, "αβ⋯ε".len())]
930// );
931
932// view.move_up(&MoveUp, cx);
933// assert_eq!(
934// view.selections.display_ranges(cx),
935// &[empty_range(1, "ab⋯e".len())]
936// );
937// view.move_down(&MoveDown, cx);
938// assert_eq!(
939// view.selections.display_ranges(cx),
940// &[empty_range(2, "αβ⋯ε".len())]
941// );
942// view.move_up(&MoveUp, cx);
943// assert_eq!(
944// view.selections.display_ranges(cx),
945// &[empty_range(1, "ab⋯e".len())]
946// );
947
948// view.move_up(&MoveUp, cx);
949// assert_eq!(
950// view.selections.display_ranges(cx),
951// &[empty_range(0, "ⓐⓑ".len())]
952// );
953// view.move_left(&MoveLeft, cx);
954// assert_eq!(
955// view.selections.display_ranges(cx),
956// &[empty_range(0, "ⓐ".len())]
957// );
958// view.move_left(&MoveLeft, cx);
959// assert_eq!(
960// view.selections.display_ranges(cx),
961// &[empty_range(0, "".len())]
962// );
963// });
964// }
965
966// #[gpui::test]
967// fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
968// init_test(cx, |_| {});
969
970// let view = cx
971// .add_window(|cx| {
972// let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
973// build_editor(buffer.clone(), cx)
974// })
975// .root(cx);
976// view.update(cx, |view, cx| {
977// view.change_selections(None, cx, |s| {
978// s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
979// });
980// view.move_down(&MoveDown, cx);
981// assert_eq!(
982// view.selections.display_ranges(cx),
983// &[empty_range(1, "abcd".len())]
984// );
985
986// view.move_down(&MoveDown, cx);
987// assert_eq!(
988// view.selections.display_ranges(cx),
989// &[empty_range(2, "αβγ".len())]
990// );
991
992// view.move_down(&MoveDown, cx);
993// assert_eq!(
994// view.selections.display_ranges(cx),
995// &[empty_range(3, "abcd".len())]
996// );
997
998// view.move_down(&MoveDown, cx);
999// assert_eq!(
1000// view.selections.display_ranges(cx),
1001// &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
1002// );
1003
1004// view.move_up(&MoveUp, cx);
1005// assert_eq!(
1006// view.selections.display_ranges(cx),
1007// &[empty_range(3, "abcd".len())]
1008// );
1009
1010// view.move_up(&MoveUp, cx);
1011// assert_eq!(
1012// view.selections.display_ranges(cx),
1013// &[empty_range(2, "αβγ".len())]
1014// );
1015// });
1016// }
1017
1018// #[gpui::test]
1019// fn test_beginning_end_of_line(cx: &mut TestAppContext) {
1020// init_test(cx, |_| {});
1021
1022// let view = cx
1023// .add_window(|cx| {
1024// let buffer = MultiBuffer::build_simple("abc\n def", cx);
1025// build_editor(buffer, cx)
1026// })
1027// .root(cx);
1028// view.update(cx, |view, cx| {
1029// view.change_selections(None, cx, |s| {
1030// s.select_display_ranges([
1031// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
1032// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1033// ]);
1034// });
1035// });
1036
1037// view.update(cx, |view, cx| {
1038// view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1039// assert_eq!(
1040// view.selections.display_ranges(cx),
1041// &[
1042// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1043// DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1044// ]
1045// );
1046// });
1047
1048// view.update(cx, |view, cx| {
1049// view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1050// assert_eq!(
1051// view.selections.display_ranges(cx),
1052// &[
1053// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1054// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1055// ]
1056// );
1057// });
1058
1059// view.update(cx, |view, cx| {
1060// view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1061// assert_eq!(
1062// view.selections.display_ranges(cx),
1063// &[
1064// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1065// DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1066// ]
1067// );
1068// });
1069
1070// view.update(cx, |view, cx| {
1071// view.move_to_end_of_line(&MoveToEndOfLine, cx);
1072// assert_eq!(
1073// view.selections.display_ranges(cx),
1074// &[
1075// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1076// DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1077// ]
1078// );
1079// });
1080
1081// // Moving to the end of line again is a no-op.
1082// view.update(cx, |view, cx| {
1083// view.move_to_end_of_line(&MoveToEndOfLine, cx);
1084// assert_eq!(
1085// view.selections.display_ranges(cx),
1086// &[
1087// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1088// DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1089// ]
1090// );
1091// });
1092
1093// view.update(cx, |view, cx| {
1094// view.move_left(&MoveLeft, cx);
1095// view.select_to_beginning_of_line(
1096// &SelectToBeginningOfLine {
1097// stop_at_soft_wraps: true,
1098// },
1099// cx,
1100// );
1101// assert_eq!(
1102// view.selections.display_ranges(cx),
1103// &[
1104// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1105// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1106// ]
1107// );
1108// });
1109
1110// view.update(cx, |view, cx| {
1111// view.select_to_beginning_of_line(
1112// &SelectToBeginningOfLine {
1113// stop_at_soft_wraps: true,
1114// },
1115// cx,
1116// );
1117// assert_eq!(
1118// view.selections.display_ranges(cx),
1119// &[
1120// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1121// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1122// ]
1123// );
1124// });
1125
1126// view.update(cx, |view, cx| {
1127// view.select_to_beginning_of_line(
1128// &SelectToBeginningOfLine {
1129// stop_at_soft_wraps: true,
1130// },
1131// cx,
1132// );
1133// assert_eq!(
1134// view.selections.display_ranges(cx),
1135// &[
1136// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1137// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1138// ]
1139// );
1140// });
1141
1142// view.update(cx, |view, cx| {
1143// view.select_to_end_of_line(
1144// &SelectToEndOfLine {
1145// stop_at_soft_wraps: true,
1146// },
1147// cx,
1148// );
1149// assert_eq!(
1150// view.selections.display_ranges(cx),
1151// &[
1152// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1153// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1154// ]
1155// );
1156// });
1157
1158// view.update(cx, |view, cx| {
1159// view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1160// assert_eq!(view.display_text(cx), "ab\n de");
1161// assert_eq!(
1162// view.selections.display_ranges(cx),
1163// &[
1164// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1165// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1166// ]
1167// );
1168// });
1169
1170// view.update(cx, |view, cx| {
1171// view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1172// assert_eq!(view.display_text(cx), "\n");
1173// assert_eq!(
1174// view.selections.display_ranges(cx),
1175// &[
1176// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1177// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1178// ]
1179// );
1180// });
1181// }
1182
1183// #[gpui::test]
1184// fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1185// init_test(cx, |_| {});
1186
1187// let view = cx
1188// .add_window(|cx| {
1189// let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
1190// build_editor(buffer, cx)
1191// })
1192// .root(cx);
1193// view.update(cx, |view, cx| {
1194// view.change_selections(None, cx, |s| {
1195// s.select_display_ranges([
1196// DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1197// DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1198// ])
1199// });
1200
1201// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1202// assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
1203
1204// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1205// assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx);
1206
1207// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1208// assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx);
1209
1210// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1211// assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
1212
1213// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1214// assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx);
1215
1216// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1217// assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx);
1218
1219// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1220// assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx);
1221
1222// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1223// assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx);
1224
1225// view.move_right(&MoveRight, cx);
1226// view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1227// assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
1228
1229// view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1230// assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx);
1231
1232// view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1233// assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx);
1234// });
1235// }
1236
1237// #[gpui::test]
1238// fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1239// init_test(cx, |_| {});
1240
1241// let view = cx
1242// .add_window(|cx| {
1243// let buffer =
1244// MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
1245// build_editor(buffer, cx)
1246// })
1247// .root(cx);
1248
1249// view.update(cx, |view, cx| {
1250// view.set_wrap_width(Some(140.), cx);
1251// assert_eq!(
1252// view.display_text(cx),
1253// "use one::{\n two::three::\n four::five\n};"
1254// );
1255
1256// view.change_selections(None, cx, |s| {
1257// s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1258// });
1259
1260// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1261// assert_eq!(
1262// view.selections.display_ranges(cx),
1263// &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1264// );
1265
1266// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1267// assert_eq!(
1268// view.selections.display_ranges(cx),
1269// &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1270// );
1271
1272// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1273// assert_eq!(
1274// view.selections.display_ranges(cx),
1275// &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1276// );
1277
1278// view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1279// assert_eq!(
1280// view.selections.display_ranges(cx),
1281// &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1282// );
1283
1284// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1285// assert_eq!(
1286// view.selections.display_ranges(cx),
1287// &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1288// );
1289
1290// view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1291// assert_eq!(
1292// view.selections.display_ranges(cx),
1293// &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1294// );
1295// });
1296// }
1297
1298// #[gpui::test]
1299// async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1300// init_test(cx, |_| {});
1301// let mut cx = EditorTestContext::new(cx).await;
1302
1303// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1304// let window = cx.window;
1305// window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
1306
1307// cx.set_state(
1308// &r#"ˇone
1309// two
1310
1311// three
1312// fourˇ
1313// five
1314
1315// six"#
1316// .unindent(),
1317// );
1318
1319// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1320// cx.assert_editor_state(
1321// &r#"one
1322// two
1323// ˇ
1324// three
1325// four
1326// five
1327// ˇ
1328// six"#
1329// .unindent(),
1330// );
1331
1332// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1333// cx.assert_editor_state(
1334// &r#"one
1335// two
1336
1337// three
1338// four
1339// five
1340// ˇ
1341// sixˇ"#
1342// .unindent(),
1343// );
1344
1345// cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1346// cx.assert_editor_state(
1347// &r#"one
1348// two
1349
1350// three
1351// four
1352// five
1353
1354// sixˇ"#
1355// .unindent(),
1356// );
1357
1358// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1359// cx.assert_editor_state(
1360// &r#"one
1361// two
1362
1363// three
1364// four
1365// five
1366// ˇ
1367// six"#
1368// .unindent(),
1369// );
1370
1371// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1372// cx.assert_editor_state(
1373// &r#"one
1374// two
1375// ˇ
1376// three
1377// four
1378// five
1379
1380// six"#
1381// .unindent(),
1382// );
1383
1384// cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1385// cx.assert_editor_state(
1386// &r#"ˇone
1387// two
1388
1389// three
1390// four
1391// five
1392
1393// six"#
1394// .unindent(),
1395// );
1396// }
1397
1398// #[gpui::test]
1399// async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
1400// init_test(cx, |_| {});
1401// let mut cx = EditorTestContext::new(cx).await;
1402// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1403// let window = cx.window;
1404// window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
1405
1406// cx.set_state(
1407// &r#"ˇone
1408// two
1409// three
1410// four
1411// five
1412// six
1413// seven
1414// eight
1415// nine
1416// ten
1417// "#,
1418// );
1419
1420// cx.update_editor(|editor, cx| {
1421// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
1422// editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1423// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
1424// editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1425// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
1426// editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
1427// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
1428
1429// editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
1430// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.));
1431// editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
1432// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
1433// });
1434// }
1435
1436// #[gpui::test]
1437// async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
1438// init_test(cx, |_| {});
1439// let mut cx = EditorTestContext::new(cx).await;
1440
1441// let line_height = cx.update_editor(|editor, cx| {
1442// editor.set_vertical_scroll_margin(2, cx);
1443// editor.style(cx).text.line_height(cx.font_cache())
1444// });
1445
1446// let window = cx.window;
1447// window.simulate_resize(vec2f(1000., 6.0 * line_height), &mut cx);
1448
1449// cx.set_state(
1450// &r#"ˇone
1451// two
1452// three
1453// four
1454// five
1455// six
1456// seven
1457// eight
1458// nine
1459// ten
1460// "#,
1461// );
1462// cx.update_editor(|editor, cx| {
1463// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.0));
1464// });
1465
1466// // Add a cursor below the visible area. Since both cursors cannot fit
1467// // on screen, the editor autoscrolls to reveal the newest cursor, and
1468// // allows the vertical scroll margin below that cursor.
1469// cx.update_editor(|editor, cx| {
1470// editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1471// selections.select_ranges([
1472// Point::new(0, 0)..Point::new(0, 0),
1473// Point::new(6, 0)..Point::new(6, 0),
1474// ]);
1475// })
1476// });
1477// cx.update_editor(|editor, cx| {
1478// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
1479// });
1480
1481// // Move down. The editor cursor scrolls down to track the newest cursor.
1482// cx.update_editor(|editor, cx| {
1483// editor.move_down(&Default::default(), cx);
1484// });
1485// cx.update_editor(|editor, cx| {
1486// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 4.0));
1487// });
1488
1489// // Add a cursor above the visible area. Since both cursors fit on screen,
1490// // the editor scrolls to show both.
1491// cx.update_editor(|editor, cx| {
1492// editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1493// selections.select_ranges([
1494// Point::new(1, 0)..Point::new(1, 0),
1495// Point::new(6, 0)..Point::new(6, 0),
1496// ]);
1497// })
1498// });
1499// cx.update_editor(|editor, cx| {
1500// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.0));
1501// });
1502// }
1503
1504// #[gpui::test]
1505// async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1506// init_test(cx, |_| {});
1507// let mut cx = EditorTestContext::new(cx).await;
1508
1509// let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1510// let window = cx.window;
1511// window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
1512
1513// cx.set_state(
1514// &r#"
1515// ˇone
1516// two
1517// threeˇ
1518// four
1519// five
1520// six
1521// seven
1522// eight
1523// nine
1524// ten
1525// "#
1526// .unindent(),
1527// );
1528
1529// cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1530// cx.assert_editor_state(
1531// &r#"
1532// one
1533// two
1534// three
1535// ˇfour
1536// five
1537// sixˇ
1538// seven
1539// eight
1540// nine
1541// ten
1542// "#
1543// .unindent(),
1544// );
1545
1546// cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1547// cx.assert_editor_state(
1548// &r#"
1549// one
1550// two
1551// three
1552// four
1553// five
1554// six
1555// ˇseven
1556// eight
1557// nineˇ
1558// ten
1559// "#
1560// .unindent(),
1561// );
1562
1563// cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1564// cx.assert_editor_state(
1565// &r#"
1566// one
1567// two
1568// three
1569// ˇfour
1570// five
1571// sixˇ
1572// seven
1573// eight
1574// nine
1575// ten
1576// "#
1577// .unindent(),
1578// );
1579
1580// cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1581// cx.assert_editor_state(
1582// &r#"
1583// ˇone
1584// two
1585// threeˇ
1586// four
1587// five
1588// six
1589// seven
1590// eight
1591// nine
1592// ten
1593// "#
1594// .unindent(),
1595// );
1596
1597// // Test select collapsing
1598// cx.update_editor(|editor, cx| {
1599// editor.move_page_down(&MovePageDown::default(), cx);
1600// editor.move_page_down(&MovePageDown::default(), cx);
1601// editor.move_page_down(&MovePageDown::default(), cx);
1602// });
1603// cx.assert_editor_state(
1604// &r#"
1605// one
1606// two
1607// three
1608// four
1609// five
1610// six
1611// seven
1612// eight
1613// nine
1614// ˇten
1615// ˇ"#
1616// .unindent(),
1617// );
1618// }
1619
1620// #[gpui::test]
1621// async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1622// init_test(cx, |_| {});
1623// let mut cx = EditorTestContext::new(cx).await;
1624// cx.set_state("one «two threeˇ» four");
1625// cx.update_editor(|editor, cx| {
1626// editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1627// assert_eq!(editor.text(cx), " four");
1628// });
1629// }
1630
1631// #[gpui::test]
1632// fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1633// init_test(cx, |_| {});
1634
1635// let view = cx
1636// .add_window(|cx| {
1637// let buffer = MultiBuffer::build_simple("one two three four", cx);
1638// build_editor(buffer.clone(), cx)
1639// })
1640// .root(cx);
1641
1642// view.update(cx, |view, cx| {
1643// view.change_selections(None, cx, |s| {
1644// s.select_display_ranges([
1645// // an empty selection - the preceding word fragment is deleted
1646// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1647// // characters selected - they are deleted
1648// DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1649// ])
1650// });
1651// view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1652// assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1653// });
1654
1655// view.update(cx, |view, cx| {
1656// view.change_selections(None, cx, |s| {
1657// s.select_display_ranges([
1658// // an empty selection - the following word fragment is deleted
1659// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1660// // characters selected - they are deleted
1661// DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1662// ])
1663// });
1664// view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1665// assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1666// });
1667// }
1668
1669// #[gpui::test]
1670// fn test_newline(cx: &mut TestAppContext) {
1671// init_test(cx, |_| {});
1672
1673// let view = cx
1674// .add_window(|cx| {
1675// let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
1676// build_editor(buffer.clone(), cx)
1677// })
1678// .root(cx);
1679
1680// view.update(cx, |view, cx| {
1681// view.change_selections(None, cx, |s| {
1682// s.select_display_ranges([
1683// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1684// DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1685// DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1686// ])
1687// });
1688
1689// view.newline(&Newline, cx);
1690// assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
1691// });
1692// }
1693
1694// #[gpui::test]
1695// fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1696// init_test(cx, |_| {});
1697
1698// let editor = cx
1699// .add_window(|cx| {
1700// let buffer = MultiBuffer::build_simple(
1701// "
1702// a
1703// b(
1704// X
1705// )
1706// c(
1707// X
1708// )
1709// "
1710// .unindent()
1711// .as_str(),
1712// cx,
1713// );
1714// let mut editor = build_editor(buffer.clone(), cx);
1715// editor.change_selections(None, cx, |s| {
1716// s.select_ranges([
1717// Point::new(2, 4)..Point::new(2, 5),
1718// Point::new(5, 4)..Point::new(5, 5),
1719// ])
1720// });
1721// editor
1722// })
1723// .root(cx);
1724
1725// editor.update(cx, |editor, cx| {
1726// // Edit the buffer directly, deleting ranges surrounding the editor's selections
1727// editor.buffer.update(cx, |buffer, cx| {
1728// buffer.edit(
1729// [
1730// (Point::new(1, 2)..Point::new(3, 0), ""),
1731// (Point::new(4, 2)..Point::new(6, 0), ""),
1732// ],
1733// None,
1734// cx,
1735// );
1736// assert_eq!(
1737// buffer.read(cx).text(),
1738// "
1739// a
1740// b()
1741// c()
1742// "
1743// .unindent()
1744// );
1745// });
1746// assert_eq!(
1747// editor.selections.ranges(cx),
1748// &[
1749// Point::new(1, 2)..Point::new(1, 2),
1750// Point::new(2, 2)..Point::new(2, 2),
1751// ],
1752// );
1753
1754// editor.newline(&Newline, cx);
1755// assert_eq!(
1756// editor.text(cx),
1757// "
1758// a
1759// b(
1760// )
1761// c(
1762// )
1763// "
1764// .unindent()
1765// );
1766
1767// // The selections are moved after the inserted newlines
1768// assert_eq!(
1769// editor.selections.ranges(cx),
1770// &[
1771// Point::new(2, 0)..Point::new(2, 0),
1772// Point::new(4, 0)..Point::new(4, 0),
1773// ],
1774// );
1775// });
1776// }
1777
1778// #[gpui::test]
1779// async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1780// init_test(cx, |settings| {
1781// settings.defaults.tab_size = NonZeroU32::new(4)
1782// });
1783
1784// let language = Arc::new(
1785// Language::new(
1786// LanguageConfig::default(),
1787// Some(tree_sitter_rust::language()),
1788// )
1789// .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1790// .unwrap(),
1791// );
1792
1793// let mut cx = EditorTestContext::new(cx).await;
1794// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1795// cx.set_state(indoc! {"
1796// const a: ˇA = (
1797// (ˇ
1798// «const_functionˇ»(ˇ),
1799// so«mˇ»et«hˇ»ing_ˇelse,ˇ
1800// )ˇ
1801// ˇ);ˇ
1802// "});
1803
1804// cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1805// cx.assert_editor_state(indoc! {"
1806// ˇ
1807// const a: A = (
1808// ˇ
1809// (
1810// ˇ
1811// ˇ
1812// const_function(),
1813// ˇ
1814// ˇ
1815// ˇ
1816// ˇ
1817// something_else,
1818// ˇ
1819// )
1820// ˇ
1821// ˇ
1822// );
1823// "});
1824// }
1825
1826// #[gpui::test]
1827// async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1828// init_test(cx, |settings| {
1829// settings.defaults.tab_size = NonZeroU32::new(4)
1830// });
1831
1832// let language = Arc::new(
1833// Language::new(
1834// LanguageConfig::default(),
1835// Some(tree_sitter_rust::language()),
1836// )
1837// .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1838// .unwrap(),
1839// );
1840
1841// let mut cx = EditorTestContext::new(cx).await;
1842// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1843// cx.set_state(indoc! {"
1844// const a: ˇA = (
1845// (ˇ
1846// «const_functionˇ»(ˇ),
1847// so«mˇ»et«hˇ»ing_ˇelse,ˇ
1848// )ˇ
1849// ˇ);ˇ
1850// "});
1851
1852// cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1853// cx.assert_editor_state(indoc! {"
1854// const a: A = (
1855// ˇ
1856// (
1857// ˇ
1858// const_function(),
1859// ˇ
1860// ˇ
1861// something_else,
1862// ˇ
1863// ˇ
1864// ˇ
1865// ˇ
1866// )
1867// ˇ
1868// );
1869// ˇ
1870// ˇ
1871// "});
1872// }
1873
1874// #[gpui::test]
1875// async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
1876// init_test(cx, |settings| {
1877// settings.defaults.tab_size = NonZeroU32::new(4)
1878// });
1879
1880// let language = Arc::new(Language::new(
1881// LanguageConfig {
1882// line_comment: Some("//".into()),
1883// ..LanguageConfig::default()
1884// },
1885// None,
1886// ));
1887// {
1888// let mut cx = EditorTestContext::new(cx).await;
1889// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1890// cx.set_state(indoc! {"
1891// // Fooˇ
1892// "});
1893
1894// cx.update_editor(|e, cx| e.newline(&Newline, cx));
1895// cx.assert_editor_state(indoc! {"
1896// // Foo
1897// //ˇ
1898// "});
1899// // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
1900// cx.set_state(indoc! {"
1901// ˇ// Foo
1902// "});
1903// cx.update_editor(|e, cx| e.newline(&Newline, cx));
1904// cx.assert_editor_state(indoc! {"
1905
1906// ˇ// Foo
1907// "});
1908// }
1909// // Ensure that comment continuations can be disabled.
1910// update_test_language_settings(cx, |settings| {
1911// settings.defaults.extend_comment_on_newline = Some(false);
1912// });
1913// let mut cx = EditorTestContext::new(cx).await;
1914// cx.set_state(indoc! {"
1915// // Fooˇ
1916// "});
1917// cx.update_editor(|e, cx| e.newline(&Newline, cx));
1918// cx.assert_editor_state(indoc! {"
1919// // Foo
1920// ˇ
1921// "});
1922// }
1923
1924// #[gpui::test]
1925// fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1926// init_test(cx, |_| {});
1927
1928// let editor = cx
1929// .add_window(|cx| {
1930// let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1931// let mut editor = build_editor(buffer.clone(), cx);
1932// editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1933// editor
1934// })
1935// .root(cx);
1936
1937// editor.update(cx, |editor, cx| {
1938// // Edit the buffer directly, deleting ranges surrounding the editor's selections
1939// editor.buffer.update(cx, |buffer, cx| {
1940// buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1941// assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1942// });
1943// assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1944
1945// editor.insert("Z", cx);
1946// assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1947
1948// // The selections are moved after the inserted characters
1949// assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1950// });
1951// }
1952
1953// #[gpui::test]
1954// async fn test_tab(cx: &mut gpui::TestAppContext) {
1955// init_test(cx, |settings| {
1956// settings.defaults.tab_size = NonZeroU32::new(3)
1957// });
1958
1959// let mut cx = EditorTestContext::new(cx).await;
1960// cx.set_state(indoc! {"
1961// ˇabˇc
1962// ˇ🏀ˇ🏀ˇefg
1963// dˇ
1964// "});
1965// cx.update_editor(|e, cx| e.tab(&Tab, cx));
1966// cx.assert_editor_state(indoc! {"
1967// ˇab ˇc
1968// ˇ🏀 ˇ🏀 ˇefg
1969// d ˇ
1970// "});
1971
1972// cx.set_state(indoc! {"
1973// a
1974// «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1975// "});
1976// cx.update_editor(|e, cx| e.tab(&Tab, cx));
1977// cx.assert_editor_state(indoc! {"
1978// a
1979// «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1980// "});
1981// }
1982
1983// #[gpui::test]
1984// async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1985// init_test(cx, |_| {});
1986
1987// let mut cx = EditorTestContext::new(cx).await;
1988// let language = Arc::new(
1989// Language::new(
1990// LanguageConfig::default(),
1991// Some(tree_sitter_rust::language()),
1992// )
1993// .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1994// .unwrap(),
1995// );
1996// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1997
1998// // cursors that are already at the suggested indent level insert
1999// // a soft tab. cursors that are to the left of the suggested indent
2000// // auto-indent their line.
2001// cx.set_state(indoc! {"
2002// ˇ
2003// const a: B = (
2004// c(
2005// d(
2006// ˇ
2007// )
2008// ˇ
2009// ˇ )
2010// );
2011// "});
2012// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2013// cx.assert_editor_state(indoc! {"
2014// ˇ
2015// const a: B = (
2016// c(
2017// d(
2018// ˇ
2019// )
2020// ˇ
2021// ˇ)
2022// );
2023// "});
2024
2025// // handle auto-indent when there are multiple cursors on the same line
2026// cx.set_state(indoc! {"
2027// const a: B = (
2028// c(
2029// ˇ ˇ
2030// ˇ )
2031// );
2032// "});
2033// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2034// cx.assert_editor_state(indoc! {"
2035// const a: B = (
2036// c(
2037// ˇ
2038// ˇ)
2039// );
2040// "});
2041// }
2042
2043// #[gpui::test]
2044// async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
2045// init_test(cx, |settings| {
2046// settings.defaults.tab_size = NonZeroU32::new(4)
2047// });
2048
2049// let language = Arc::new(
2050// Language::new(
2051// LanguageConfig::default(),
2052// Some(tree_sitter_rust::language()),
2053// )
2054// .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
2055// .unwrap(),
2056// );
2057
2058// let mut cx = EditorTestContext::new(cx).await;
2059// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2060// cx.set_state(indoc! {"
2061// fn a() {
2062// if b {
2063// \t ˇc
2064// }
2065// }
2066// "});
2067
2068// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2069// cx.assert_editor_state(indoc! {"
2070// fn a() {
2071// if b {
2072// ˇc
2073// }
2074// }
2075// "});
2076// }
2077
2078// #[gpui::test]
2079// async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
2080// init_test(cx, |settings| {
2081// settings.defaults.tab_size = NonZeroU32::new(4);
2082// });
2083
2084// let mut cx = EditorTestContext::new(cx).await;
2085
2086// cx.set_state(indoc! {"
2087// «oneˇ» «twoˇ»
2088// three
2089// four
2090// "});
2091// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2092// cx.assert_editor_state(indoc! {"
2093// «oneˇ» «twoˇ»
2094// three
2095// four
2096// "});
2097
2098// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2099// cx.assert_editor_state(indoc! {"
2100// «oneˇ» «twoˇ»
2101// three
2102// four
2103// "});
2104
2105// // select across line ending
2106// cx.set_state(indoc! {"
2107// one two
2108// t«hree
2109// ˇ» four
2110// "});
2111// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2112// cx.assert_editor_state(indoc! {"
2113// one two
2114// t«hree
2115// ˇ» four
2116// "});
2117
2118// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2119// cx.assert_editor_state(indoc! {"
2120// one two
2121// t«hree
2122// ˇ» four
2123// "});
2124
2125// // Ensure that indenting/outdenting works when the cursor is at column 0.
2126// cx.set_state(indoc! {"
2127// one two
2128// ˇthree
2129// four
2130// "});
2131// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2132// cx.assert_editor_state(indoc! {"
2133// one two
2134// ˇthree
2135// four
2136// "});
2137
2138// cx.set_state(indoc! {"
2139// one two
2140// ˇ three
2141// four
2142// "});
2143// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2144// cx.assert_editor_state(indoc! {"
2145// one two
2146// ˇthree
2147// four
2148// "});
2149// }
2150
2151// #[gpui::test]
2152// async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
2153// init_test(cx, |settings| {
2154// settings.defaults.hard_tabs = Some(true);
2155// });
2156
2157// let mut cx = EditorTestContext::new(cx).await;
2158
2159// // select two ranges on one line
2160// cx.set_state(indoc! {"
2161// «oneˇ» «twoˇ»
2162// three
2163// four
2164// "});
2165// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2166// cx.assert_editor_state(indoc! {"
2167// \t«oneˇ» «twoˇ»
2168// three
2169// four
2170// "});
2171// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2172// cx.assert_editor_state(indoc! {"
2173// \t\t«oneˇ» «twoˇ»
2174// three
2175// four
2176// "});
2177// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2178// cx.assert_editor_state(indoc! {"
2179// \t«oneˇ» «twoˇ»
2180// three
2181// four
2182// "});
2183// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2184// cx.assert_editor_state(indoc! {"
2185// «oneˇ» «twoˇ»
2186// three
2187// four
2188// "});
2189
2190// // select across a line ending
2191// cx.set_state(indoc! {"
2192// one two
2193// t«hree
2194// ˇ»four
2195// "});
2196// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2197// cx.assert_editor_state(indoc! {"
2198// one two
2199// \tt«hree
2200// ˇ»four
2201// "});
2202// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2203// cx.assert_editor_state(indoc! {"
2204// one two
2205// \t\tt«hree
2206// ˇ»four
2207// "});
2208// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2209// cx.assert_editor_state(indoc! {"
2210// one two
2211// \tt«hree
2212// ˇ»four
2213// "});
2214// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2215// cx.assert_editor_state(indoc! {"
2216// one two
2217// t«hree
2218// ˇ»four
2219// "});
2220
2221// // Ensure that indenting/outdenting works when the cursor is at column 0.
2222// cx.set_state(indoc! {"
2223// one two
2224// ˇthree
2225// four
2226// "});
2227// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2228// cx.assert_editor_state(indoc! {"
2229// one two
2230// ˇthree
2231// four
2232// "});
2233// cx.update_editor(|e, cx| e.tab(&Tab, cx));
2234// cx.assert_editor_state(indoc! {"
2235// one two
2236// \tˇthree
2237// four
2238// "});
2239// cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2240// cx.assert_editor_state(indoc! {"
2241// one two
2242// ˇthree
2243// four
2244// "});
2245// }
2246
2247// #[gpui::test]
2248// fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2249// init_test(cx, |settings| {
2250// settings.languages.extend([
2251// (
2252// "TOML".into(),
2253// LanguageSettingsContent {
2254// tab_size: NonZeroU32::new(2),
2255// ..Default::default()
2256// },
2257// ),
2258// (
2259// "Rust".into(),
2260// LanguageSettingsContent {
2261// tab_size: NonZeroU32::new(4),
2262// ..Default::default()
2263// },
2264// ),
2265// ]);
2266// });
2267
2268// let toml_language = Arc::new(Language::new(
2269// LanguageConfig {
2270// name: "TOML".into(),
2271// ..Default::default()
2272// },
2273// None,
2274// ));
2275// let rust_language = Arc::new(Language::new(
2276// LanguageConfig {
2277// name: "Rust".into(),
2278// ..Default::default()
2279// },
2280// None,
2281// ));
2282
2283// let toml_buffer = cx.add_model(|cx| {
2284// Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n").with_language(toml_language, cx)
2285// });
2286// let rust_buffer = cx.add_model(|cx| {
2287// Buffer::new(0, cx.model_id() as u64, "const c: usize = 3;\n")
2288// .with_language(rust_language, cx)
2289// });
2290// let multibuffer = cx.add_model(|cx| {
2291// let mut multibuffer = MultiBuffer::new(0);
2292// multibuffer.push_excerpts(
2293// toml_buffer.clone(),
2294// [ExcerptRange {
2295// context: Point::new(0, 0)..Point::new(2, 0),
2296// primary: None,
2297// }],
2298// cx,
2299// );
2300// multibuffer.push_excerpts(
2301// rust_buffer.clone(),
2302// [ExcerptRange {
2303// context: Point::new(0, 0)..Point::new(1, 0),
2304// primary: None,
2305// }],
2306// cx,
2307// );
2308// multibuffer
2309// });
2310
2311// cx.add_window(|cx| {
2312// let mut editor = build_editor(multibuffer, cx);
2313
2314// assert_eq!(
2315// editor.text(cx),
2316// indoc! {"
2317// a = 1
2318// b = 2
2319
2320// const c: usize = 3;
2321// "}
2322// );
2323
2324// select_ranges(
2325// &mut editor,
2326// indoc! {"
2327// «aˇ» = 1
2328// b = 2
2329
2330// «const c:ˇ» usize = 3;
2331// "},
2332// cx,
2333// );
2334
2335// editor.tab(&Tab, cx);
2336// assert_text_with_selections(
2337// &mut editor,
2338// indoc! {"
2339// «aˇ» = 1
2340// b = 2
2341
2342// «const c:ˇ» usize = 3;
2343// "},
2344// cx,
2345// );
2346// editor.tab_prev(&TabPrev, cx);
2347// assert_text_with_selections(
2348// &mut editor,
2349// indoc! {"
2350// «aˇ» = 1
2351// b = 2
2352
2353// «const c:ˇ» usize = 3;
2354// "},
2355// cx,
2356// );
2357
2358// editor
2359// });
2360// }
2361
2362// #[gpui::test]
2363// async fn test_backspace(cx: &mut gpui::TestAppContext) {
2364// init_test(cx, |_| {});
2365
2366// let mut cx = EditorTestContext::new(cx).await;
2367
2368// // Basic backspace
2369// cx.set_state(indoc! {"
2370// onˇe two three
2371// fou«rˇ» five six
2372// seven «ˇeight nine
2373// »ten
2374// "});
2375// cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2376// cx.assert_editor_state(indoc! {"
2377// oˇe two three
2378// fouˇ five six
2379// seven ˇten
2380// "});
2381
2382// // Test backspace inside and around indents
2383// cx.set_state(indoc! {"
2384// zero
2385// ˇone
2386// ˇtwo
2387// ˇ ˇ ˇ three
2388// ˇ ˇ four
2389// "});
2390// cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2391// cx.assert_editor_state(indoc! {"
2392// zero
2393// ˇone
2394// ˇtwo
2395// ˇ threeˇ four
2396// "});
2397
2398// // Test backspace with line_mode set to true
2399// cx.update_editor(|e, _| e.selections.line_mode = true);
2400// cx.set_state(indoc! {"
2401// The ˇquick ˇbrown
2402// fox jumps over
2403// the lazy dog
2404// ˇThe qu«ick bˇ»rown"});
2405// cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2406// cx.assert_editor_state(indoc! {"
2407// ˇfox jumps over
2408// the lazy dogˇ"});
2409// }
2410
2411// #[gpui::test]
2412// async fn test_delete(cx: &mut gpui::TestAppContext) {
2413// init_test(cx, |_| {});
2414
2415// let mut cx = EditorTestContext::new(cx).await;
2416// cx.set_state(indoc! {"
2417// onˇe two three
2418// fou«rˇ» five six
2419// seven «ˇeight nine
2420// »ten
2421// "});
2422// cx.update_editor(|e, cx| e.delete(&Delete, cx));
2423// cx.assert_editor_state(indoc! {"
2424// onˇ two three
2425// fouˇ five six
2426// seven ˇten
2427// "});
2428
2429// // Test backspace with line_mode set to true
2430// cx.update_editor(|e, _| e.selections.line_mode = true);
2431// cx.set_state(indoc! {"
2432// The ˇquick ˇbrown
2433// fox «ˇjum»ps over
2434// the lazy dog
2435// ˇThe qu«ick bˇ»rown"});
2436// cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2437// cx.assert_editor_state("ˇthe lazy dogˇ");
2438// }
2439
2440// #[gpui::test]
2441// fn test_delete_line(cx: &mut TestAppContext) {
2442// init_test(cx, |_| {});
2443
2444// let view = cx
2445// .add_window(|cx| {
2446// let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2447// build_editor(buffer, cx)
2448// })
2449// .root(cx);
2450// view.update(cx, |view, cx| {
2451// view.change_selections(None, cx, |s| {
2452// s.select_display_ranges([
2453// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2454// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2455// DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2456// ])
2457// });
2458// view.delete_line(&DeleteLine, cx);
2459// assert_eq!(view.display_text(cx), "ghi");
2460// assert_eq!(
2461// view.selections.display_ranges(cx),
2462// vec![
2463// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2464// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2465// ]
2466// );
2467// });
2468
2469// let view = cx
2470// .add_window(|cx| {
2471// let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2472// build_editor(buffer, cx)
2473// })
2474// .root(cx);
2475// view.update(cx, |view, cx| {
2476// view.change_selections(None, cx, |s| {
2477// s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2478// });
2479// view.delete_line(&DeleteLine, cx);
2480// assert_eq!(view.display_text(cx), "ghi\n");
2481// assert_eq!(
2482// view.selections.display_ranges(cx),
2483// vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2484// );
2485// });
2486// }
2487
2488// #[gpui::test]
2489// fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
2490// init_test(cx, |_| {});
2491
2492// cx.add_window(|cx| {
2493// let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2494// let mut editor = build_editor(buffer.clone(), cx);
2495// let buffer = buffer.read(cx).as_singleton().unwrap();
2496
2497// assert_eq!(
2498// editor.selections.ranges::<Point>(cx),
2499// &[Point::new(0, 0)..Point::new(0, 0)]
2500// );
2501
2502// // When on single line, replace newline at end by space
2503// editor.join_lines(&JoinLines, cx);
2504// assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2505// assert_eq!(
2506// editor.selections.ranges::<Point>(cx),
2507// &[Point::new(0, 3)..Point::new(0, 3)]
2508// );
2509
2510// // When multiple lines are selected, remove newlines that are spanned by the selection
2511// editor.change_selections(None, cx, |s| {
2512// s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2513// });
2514// editor.join_lines(&JoinLines, cx);
2515// assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2516// assert_eq!(
2517// editor.selections.ranges::<Point>(cx),
2518// &[Point::new(0, 11)..Point::new(0, 11)]
2519// );
2520
2521// // Undo should be transactional
2522// editor.undo(&Undo, cx);
2523// assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2524// assert_eq!(
2525// editor.selections.ranges::<Point>(cx),
2526// &[Point::new(0, 5)..Point::new(2, 2)]
2527// );
2528
2529// // When joining an empty line don't insert a space
2530// editor.change_selections(None, cx, |s| {
2531// s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2532// });
2533// editor.join_lines(&JoinLines, cx);
2534// assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2535// assert_eq!(
2536// editor.selections.ranges::<Point>(cx),
2537// [Point::new(2, 3)..Point::new(2, 3)]
2538// );
2539
2540// // We can remove trailing newlines
2541// editor.join_lines(&JoinLines, cx);
2542// assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2543// assert_eq!(
2544// editor.selections.ranges::<Point>(cx),
2545// [Point::new(2, 3)..Point::new(2, 3)]
2546// );
2547
2548// // We don't blow up on the last line
2549// editor.join_lines(&JoinLines, cx);
2550// assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2551// assert_eq!(
2552// editor.selections.ranges::<Point>(cx),
2553// [Point::new(2, 3)..Point::new(2, 3)]
2554// );
2555
2556// // reset to test indentation
2557// editor.buffer.update(cx, |buffer, cx| {
2558// buffer.edit(
2559// [
2560// (Point::new(1, 0)..Point::new(1, 2), " "),
2561// (Point::new(2, 0)..Point::new(2, 3), " \n\td"),
2562// ],
2563// None,
2564// cx,
2565// )
2566// });
2567
2568// // We remove any leading spaces
2569// assert_eq!(buffer.read(cx).text(), "aaa bbb\n c\n \n\td");
2570// editor.change_selections(None, cx, |s| {
2571// s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
2572// });
2573// editor.join_lines(&JoinLines, cx);
2574// assert_eq!(buffer.read(cx).text(), "aaa bbb c\n \n\td");
2575
2576// // We don't insert a space for a line containing only spaces
2577// editor.join_lines(&JoinLines, cx);
2578// assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
2579
2580// // We ignore any leading tabs
2581// editor.join_lines(&JoinLines, cx);
2582// assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
2583
2584// editor
2585// });
2586// }
2587
2588// #[gpui::test]
2589// fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
2590// init_test(cx, |_| {});
2591
2592// cx.add_window(|cx| {
2593// let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2594// let mut editor = build_editor(buffer.clone(), cx);
2595// let buffer = buffer.read(cx).as_singleton().unwrap();
2596
2597// editor.change_selections(None, cx, |s| {
2598// s.select_ranges([
2599// Point::new(0, 2)..Point::new(1, 1),
2600// Point::new(1, 2)..Point::new(1, 2),
2601// Point::new(3, 1)..Point::new(3, 2),
2602// ])
2603// });
2604
2605// editor.join_lines(&JoinLines, cx);
2606// assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
2607
2608// assert_eq!(
2609// editor.selections.ranges::<Point>(cx),
2610// [
2611// Point::new(0, 7)..Point::new(0, 7),
2612// Point::new(1, 3)..Point::new(1, 3)
2613// ]
2614// );
2615// editor
2616// });
2617// }
2618
2619// #[gpui::test]
2620// async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
2621// init_test(cx, |_| {});
2622
2623// let mut cx = EditorTestContext::new(cx).await;
2624
2625// // Test sort_lines_case_insensitive()
2626// cx.set_state(indoc! {"
2627// «z
2628// y
2629// x
2630// Z
2631// Y
2632// Xˇ»
2633// "});
2634// cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
2635// cx.assert_editor_state(indoc! {"
2636// «x
2637// X
2638// y
2639// Y
2640// z
2641// Zˇ»
2642// "});
2643
2644// // Test reverse_lines()
2645// cx.set_state(indoc! {"
2646// «5
2647// 4
2648// 3
2649// 2
2650// 1ˇ»
2651// "});
2652// cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
2653// cx.assert_editor_state(indoc! {"
2654// «1
2655// 2
2656// 3
2657// 4
2658// 5ˇ»
2659// "});
2660
2661// // Skip testing shuffle_line()
2662
2663// // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
2664// // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
2665
2666// // Don't manipulate when cursor is on single line, but expand the selection
2667// cx.set_state(indoc! {"
2668// ddˇdd
2669// ccc
2670// bb
2671// a
2672// "});
2673// cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2674// cx.assert_editor_state(indoc! {"
2675// «ddddˇ»
2676// ccc
2677// bb
2678// a
2679// "});
2680
2681// // Basic manipulate case
2682// // Start selection moves to column 0
2683// // End of selection shrinks to fit shorter line
2684// cx.set_state(indoc! {"
2685// dd«d
2686// ccc
2687// bb
2688// aaaaaˇ»
2689// "});
2690// cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2691// cx.assert_editor_state(indoc! {"
2692// «aaaaa
2693// bb
2694// ccc
2695// dddˇ»
2696// "});
2697
2698// // Manipulate case with newlines
2699// cx.set_state(indoc! {"
2700// dd«d
2701// ccc
2702
2703// bb
2704// aaaaa
2705
2706// ˇ»
2707// "});
2708// cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2709// cx.assert_editor_state(indoc! {"
2710// «
2711
2712// aaaaa
2713// bb
2714// ccc
2715// dddˇ»
2716
2717// "});
2718// }
2719
2720// #[gpui::test]
2721// async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
2722// init_test(cx, |_| {});
2723
2724// let mut cx = EditorTestContext::new(cx).await;
2725
2726// // Manipulate with multiple selections on a single line
2727// cx.set_state(indoc! {"
2728// dd«dd
2729// cˇ»c«c
2730// bb
2731// aaaˇ»aa
2732// "});
2733// cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2734// cx.assert_editor_state(indoc! {"
2735// «aaaaa
2736// bb
2737// ccc
2738// ddddˇ»
2739// "});
2740
2741// // Manipulate with multiple disjoin selections
2742// cx.set_state(indoc! {"
2743// 5«
2744// 4
2745// 3
2746// 2
2747// 1ˇ»
2748
2749// dd«dd
2750// ccc
2751// bb
2752// aaaˇ»aa
2753// "});
2754// cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2755// cx.assert_editor_state(indoc! {"
2756// «1
2757// 2
2758// 3
2759// 4
2760// 5ˇ»
2761
2762// «aaaaa
2763// bb
2764// ccc
2765// ddddˇ»
2766// "});
2767// }
2768
2769// #[gpui::test]
2770// async fn test_manipulate_text(cx: &mut TestAppContext) {
2771// init_test(cx, |_| {});
2772
2773// let mut cx = EditorTestContext::new(cx).await;
2774
2775// // Test convert_to_upper_case()
2776// cx.set_state(indoc! {"
2777// «hello worldˇ»
2778// "});
2779// cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2780// cx.assert_editor_state(indoc! {"
2781// «HELLO WORLDˇ»
2782// "});
2783
2784// // Test convert_to_lower_case()
2785// cx.set_state(indoc! {"
2786// «HELLO WORLDˇ»
2787// "});
2788// cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
2789// cx.assert_editor_state(indoc! {"
2790// «hello worldˇ»
2791// "});
2792
2793// // Test multiple line, single selection case
2794// // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
2795// cx.set_state(indoc! {"
2796// «The quick brown
2797// fox jumps over
2798// the lazy dogˇ»
2799// "});
2800// cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
2801// cx.assert_editor_state(indoc! {"
2802// «The Quick Brown
2803// Fox Jumps Over
2804// The Lazy Dogˇ»
2805// "});
2806
2807// // Test multiple line, single selection case
2808// // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
2809// cx.set_state(indoc! {"
2810// «The quick brown
2811// fox jumps over
2812// the lazy dogˇ»
2813// "});
2814// cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
2815// cx.assert_editor_state(indoc! {"
2816// «TheQuickBrown
2817// FoxJumpsOver
2818// TheLazyDogˇ»
2819// "});
2820
2821// // From here on out, test more complex cases of manipulate_text()
2822
2823// // Test no selection case - should affect words cursors are in
2824// // Cursor at beginning, middle, and end of word
2825// cx.set_state(indoc! {"
2826// ˇhello big beauˇtiful worldˇ
2827// "});
2828// cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2829// cx.assert_editor_state(indoc! {"
2830// «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
2831// "});
2832
2833// // Test multiple selections on a single line and across multiple lines
2834// cx.set_state(indoc! {"
2835// «Theˇ» quick «brown
2836// foxˇ» jumps «overˇ»
2837// the «lazyˇ» dog
2838// "});
2839// cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2840// cx.assert_editor_state(indoc! {"
2841// «THEˇ» quick «BROWN
2842// FOXˇ» jumps «OVERˇ»
2843// the «LAZYˇ» dog
2844// "});
2845
2846// // Test case where text length grows
2847// cx.set_state(indoc! {"
2848// «tschüߡ»
2849// "});
2850// cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2851// cx.assert_editor_state(indoc! {"
2852// «TSCHÜSSˇ»
2853// "});
2854
2855// // Test to make sure we don't crash when text shrinks
2856// cx.set_state(indoc! {"
2857// aaa_bbbˇ
2858// "});
2859// cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
2860// cx.assert_editor_state(indoc! {"
2861// «aaaBbbˇ»
2862// "});
2863
2864// // Test to make sure we all aware of the fact that each word can grow and shrink
2865// // Final selections should be aware of this fact
2866// cx.set_state(indoc! {"
2867// aaa_bˇbb bbˇb_ccc ˇccc_ddd
2868// "});
2869// cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
2870// cx.assert_editor_state(indoc! {"
2871// «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
2872// "});
2873// }
2874
2875// #[gpui::test]
2876// fn test_duplicate_line(cx: &mut TestAppContext) {
2877// init_test(cx, |_| {});
2878
2879// let view = cx
2880// .add_window(|cx| {
2881// let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2882// build_editor(buffer, cx)
2883// })
2884// .root(cx);
2885// view.update(cx, |view, cx| {
2886// view.change_selections(None, cx, |s| {
2887// s.select_display_ranges([
2888// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2889// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2890// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2891// DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2892// ])
2893// });
2894// view.duplicate_line(&DuplicateLine, cx);
2895// assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2896// assert_eq!(
2897// view.selections.display_ranges(cx),
2898// vec![
2899// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2900// DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2901// DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2902// DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2903// ]
2904// );
2905// });
2906
2907// let view = cx
2908// .add_window(|cx| {
2909// let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2910// build_editor(buffer, cx)
2911// })
2912// .root(cx);
2913// view.update(cx, |view, cx| {
2914// view.change_selections(None, cx, |s| {
2915// s.select_display_ranges([
2916// DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2917// DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2918// ])
2919// });
2920// view.duplicate_line(&DuplicateLine, cx);
2921// assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2922// assert_eq!(
2923// view.selections.display_ranges(cx),
2924// vec![
2925// DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2926// DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2927// ]
2928// );
2929// });
2930// }
2931
2932// #[gpui::test]
2933// fn test_move_line_up_down(cx: &mut TestAppContext) {
2934// init_test(cx, |_| {});
2935
2936// let view = cx
2937// .add_window(|cx| {
2938// let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2939// build_editor(buffer, cx)
2940// })
2941// .root(cx);
2942// view.update(cx, |view, cx| {
2943// view.fold_ranges(
2944// vec![
2945// Point::new(0, 2)..Point::new(1, 2),
2946// Point::new(2, 3)..Point::new(4, 1),
2947// Point::new(7, 0)..Point::new(8, 4),
2948// ],
2949// true,
2950// cx,
2951// );
2952// view.change_selections(None, cx, |s| {
2953// s.select_display_ranges([
2954// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2955// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2956// DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2957// DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2958// ])
2959// });
2960// assert_eq!(
2961// view.display_text(cx),
2962// "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2963// );
2964
2965// view.move_line_up(&MoveLineUp, cx);
2966// assert_eq!(
2967// view.display_text(cx),
2968// "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2969// );
2970// assert_eq!(
2971// view.selections.display_ranges(cx),
2972// vec![
2973// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2974// DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2975// DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2976// DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2977// ]
2978// );
2979// });
2980
2981// view.update(cx, |view, cx| {
2982// view.move_line_down(&MoveLineDown, cx);
2983// assert_eq!(
2984// view.display_text(cx),
2985// "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2986// );
2987// assert_eq!(
2988// view.selections.display_ranges(cx),
2989// vec![
2990// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2991// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2992// DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2993// DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2994// ]
2995// );
2996// });
2997
2998// view.update(cx, |view, cx| {
2999// view.move_line_down(&MoveLineDown, cx);
3000// assert_eq!(
3001// view.display_text(cx),
3002// "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3003// );
3004// assert_eq!(
3005// view.selections.display_ranges(cx),
3006// vec![
3007// DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3008// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3009// DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3010// DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3011// ]
3012// );
3013// });
3014
3015// view.update(cx, |view, cx| {
3016// view.move_line_up(&MoveLineUp, cx);
3017// assert_eq!(
3018// view.display_text(cx),
3019// "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3020// );
3021// assert_eq!(
3022// view.selections.display_ranges(cx),
3023// vec![
3024// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3025// DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3026// DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3027// DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3028// ]
3029// );
3030// });
3031// }
3032
3033// #[gpui::test]
3034// fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3035// init_test(cx, |_| {});
3036
3037// let editor = cx
3038// .add_window(|cx| {
3039// let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3040// build_editor(buffer, cx)
3041// })
3042// .root(cx);
3043// editor.update(cx, |editor, cx| {
3044// let snapshot = editor.buffer.read(cx).snapshot(cx);
3045// editor.insert_blocks(
3046// [BlockProperties {
3047// style: BlockStyle::Fixed,
3048// position: snapshot.anchor_after(Point::new(2, 0)),
3049// disposition: BlockDisposition::Below,
3050// height: 1,
3051// render: Arc::new(|_| Empty::new().into_any()),
3052// }],
3053// Some(Autoscroll::fit()),
3054// cx,
3055// );
3056// editor.change_selections(None, cx, |s| {
3057// s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3058// });
3059// editor.move_line_down(&MoveLineDown, cx);
3060// });
3061// }
3062
3063// #[gpui::test]
3064// fn test_transpose(cx: &mut TestAppContext) {
3065// init_test(cx, |_| {});
3066
3067// _ = cx.add_window(|cx| {
3068// let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3069
3070// editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3071// editor.transpose(&Default::default(), cx);
3072// assert_eq!(editor.text(cx), "bac");
3073// assert_eq!(editor.selections.ranges(cx), [2..2]);
3074
3075// editor.transpose(&Default::default(), cx);
3076// assert_eq!(editor.text(cx), "bca");
3077// assert_eq!(editor.selections.ranges(cx), [3..3]);
3078
3079// editor.transpose(&Default::default(), cx);
3080// assert_eq!(editor.text(cx), "bac");
3081// assert_eq!(editor.selections.ranges(cx), [3..3]);
3082
3083// editor
3084// });
3085
3086// _ = cx.add_window(|cx| {
3087// let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3088
3089// editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3090// editor.transpose(&Default::default(), cx);
3091// assert_eq!(editor.text(cx), "acb\nde");
3092// assert_eq!(editor.selections.ranges(cx), [3..3]);
3093
3094// editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3095// editor.transpose(&Default::default(), cx);
3096// assert_eq!(editor.text(cx), "acbd\ne");
3097// assert_eq!(editor.selections.ranges(cx), [5..5]);
3098
3099// editor.transpose(&Default::default(), cx);
3100// assert_eq!(editor.text(cx), "acbde\n");
3101// assert_eq!(editor.selections.ranges(cx), [6..6]);
3102
3103// editor.transpose(&Default::default(), cx);
3104// assert_eq!(editor.text(cx), "acbd\ne");
3105// assert_eq!(editor.selections.ranges(cx), [6..6]);
3106
3107// editor
3108// });
3109
3110// _ = cx.add_window(|cx| {
3111// let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3112
3113// editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3114// editor.transpose(&Default::default(), cx);
3115// assert_eq!(editor.text(cx), "bacd\ne");
3116// assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3117
3118// editor.transpose(&Default::default(), cx);
3119// assert_eq!(editor.text(cx), "bcade\n");
3120// assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3121
3122// editor.transpose(&Default::default(), cx);
3123// assert_eq!(editor.text(cx), "bcda\ne");
3124// assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3125
3126// editor.transpose(&Default::default(), cx);
3127// assert_eq!(editor.text(cx), "bcade\n");
3128// assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3129
3130// editor.transpose(&Default::default(), cx);
3131// assert_eq!(editor.text(cx), "bcaed\n");
3132// assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3133
3134// editor
3135// });
3136
3137// _ = cx.add_window(|cx| {
3138// let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3139
3140// editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3141// editor.transpose(&Default::default(), cx);
3142// assert_eq!(editor.text(cx), "🏀🍐✋");
3143// assert_eq!(editor.selections.ranges(cx), [8..8]);
3144
3145// editor.transpose(&Default::default(), cx);
3146// assert_eq!(editor.text(cx), "🏀✋🍐");
3147// assert_eq!(editor.selections.ranges(cx), [11..11]);
3148
3149// editor.transpose(&Default::default(), cx);
3150// assert_eq!(editor.text(cx), "🏀🍐✋");
3151// assert_eq!(editor.selections.ranges(cx), [11..11]);
3152
3153// editor
3154// });
3155// }
3156
3157// #[gpui::test]
3158// async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3159// init_test(cx, |_| {});
3160
3161// let mut cx = EditorTestContext::new(cx).await;
3162
3163// cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3164// cx.update_editor(|e, cx| e.cut(&Cut, cx));
3165// cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3166
3167// // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3168// cx.set_state("two ˇfour ˇsix ˇ");
3169// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3170// cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3171
3172// // Paste again but with only two cursors. Since the number of cursors doesn't
3173// // match the number of slices in the clipboard, the entire clipboard text
3174// // is pasted at each cursor.
3175// cx.set_state("ˇtwo one✅ four three six five ˇ");
3176// cx.update_editor(|e, cx| {
3177// e.handle_input("( ", cx);
3178// e.paste(&Paste, cx);
3179// e.handle_input(") ", cx);
3180// });
3181// cx.assert_editor_state(
3182// &([
3183// "( one✅ ",
3184// "three ",
3185// "five ) ˇtwo one✅ four three six five ( one✅ ",
3186// "three ",
3187// "five ) ˇ",
3188// ]
3189// .join("\n")),
3190// );
3191
3192// // Cut with three selections, one of which is full-line.
3193// cx.set_state(indoc! {"
3194// 1«2ˇ»3
3195// 4ˇ567
3196// «8ˇ»9"});
3197// cx.update_editor(|e, cx| e.cut(&Cut, cx));
3198// cx.assert_editor_state(indoc! {"
3199// 1ˇ3
3200// ˇ9"});
3201
3202// // Paste with three selections, noticing how the copied selection that was full-line
3203// // gets inserted before the second cursor.
3204// cx.set_state(indoc! {"
3205// 1ˇ3
3206// 9ˇ
3207// «oˇ»ne"});
3208// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3209// cx.assert_editor_state(indoc! {"
3210// 12ˇ3
3211// 4567
3212// 9ˇ
3213// 8ˇne"});
3214
3215// // Copy with a single cursor only, which writes the whole line into the clipboard.
3216// cx.set_state(indoc! {"
3217// The quick brown
3218// fox juˇmps over
3219// the lazy dog"});
3220// cx.update_editor(|e, cx| e.copy(&Copy, cx));
3221// cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
3222
3223// // Paste with three selections, noticing how the copied full-line selection is inserted
3224// // before the empty selections but replaces the selection that is non-empty.
3225// cx.set_state(indoc! {"
3226// Tˇhe quick brown
3227// «foˇ»x jumps over
3228// tˇhe lazy dog"});
3229// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3230// cx.assert_editor_state(indoc! {"
3231// fox jumps over
3232// Tˇhe quick brown
3233// fox jumps over
3234// ˇx jumps over
3235// fox jumps over
3236// tˇhe lazy dog"});
3237// }
3238
3239// #[gpui::test]
3240// async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3241// init_test(cx, |_| {});
3242
3243// let mut cx = EditorTestContext::new(cx).await;
3244// let language = Arc::new(Language::new(
3245// LanguageConfig::default(),
3246// Some(tree_sitter_rust::language()),
3247// ));
3248// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3249
3250// // Cut an indented block, without the leading whitespace.
3251// cx.set_state(indoc! {"
3252// const a: B = (
3253// c(),
3254// «d(
3255// e,
3256// f
3257// )ˇ»
3258// );
3259// "});
3260// cx.update_editor(|e, cx| e.cut(&Cut, cx));
3261// cx.assert_editor_state(indoc! {"
3262// const a: B = (
3263// c(),
3264// ˇ
3265// );
3266// "});
3267
3268// // Paste it at the same position.
3269// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3270// cx.assert_editor_state(indoc! {"
3271// const a: B = (
3272// c(),
3273// d(
3274// e,
3275// f
3276// )ˇ
3277// );
3278// "});
3279
3280// // Paste it at a line with a lower indent level.
3281// cx.set_state(indoc! {"
3282// ˇ
3283// const a: B = (
3284// c(),
3285// );
3286// "});
3287// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3288// cx.assert_editor_state(indoc! {"
3289// d(
3290// e,
3291// f
3292// )ˇ
3293// const a: B = (
3294// c(),
3295// );
3296// "});
3297
3298// // Cut an indented block, with the leading whitespace.
3299// cx.set_state(indoc! {"
3300// const a: B = (
3301// c(),
3302// « d(
3303// e,
3304// f
3305// )
3306// ˇ»);
3307// "});
3308// cx.update_editor(|e, cx| e.cut(&Cut, cx));
3309// cx.assert_editor_state(indoc! {"
3310// const a: B = (
3311// c(),
3312// ˇ);
3313// "});
3314
3315// // Paste it at the same position.
3316// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3317// cx.assert_editor_state(indoc! {"
3318// const a: B = (
3319// c(),
3320// d(
3321// e,
3322// f
3323// )
3324// ˇ);
3325// "});
3326
3327// // Paste it at a line with a higher indent level.
3328// cx.set_state(indoc! {"
3329// const a: B = (
3330// c(),
3331// d(
3332// e,
3333// fˇ
3334// )
3335// );
3336// "});
3337// cx.update_editor(|e, cx| e.paste(&Paste, cx));
3338// cx.assert_editor_state(indoc! {"
3339// const a: B = (
3340// c(),
3341// d(
3342// e,
3343// f d(
3344// e,
3345// f
3346// )
3347// ˇ
3348// )
3349// );
3350// "});
3351// }
3352
3353// #[gpui::test]
3354// fn test_select_all(cx: &mut TestAppContext) {
3355// init_test(cx, |_| {});
3356
3357// let view = cx
3358// .add_window(|cx| {
3359// let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3360// build_editor(buffer, cx)
3361// })
3362// .root(cx);
3363// view.update(cx, |view, cx| {
3364// view.select_all(&SelectAll, cx);
3365// assert_eq!(
3366// view.selections.display_ranges(cx),
3367// &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3368// );
3369// });
3370// }
3371
3372// #[gpui::test]
3373// fn test_select_line(cx: &mut TestAppContext) {
3374// init_test(cx, |_| {});
3375
3376// let view = cx
3377// .add_window(|cx| {
3378// let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3379// build_editor(buffer, cx)
3380// })
3381// .root(cx);
3382// view.update(cx, |view, cx| {
3383// view.change_selections(None, cx, |s| {
3384// s.select_display_ranges([
3385// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3386// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3387// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3388// DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3389// ])
3390// });
3391// view.select_line(&SelectLine, cx);
3392// assert_eq!(
3393// view.selections.display_ranges(cx),
3394// vec![
3395// DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3396// DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3397// ]
3398// );
3399// });
3400
3401// view.update(cx, |view, cx| {
3402// view.select_line(&SelectLine, cx);
3403// assert_eq!(
3404// view.selections.display_ranges(cx),
3405// vec![
3406// DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3407// DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3408// ]
3409// );
3410// });
3411
3412// view.update(cx, |view, cx| {
3413// view.select_line(&SelectLine, cx);
3414// assert_eq!(
3415// view.selections.display_ranges(cx),
3416// vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3417// );
3418// });
3419// }
3420
3421// #[gpui::test]
3422// fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3423// init_test(cx, |_| {});
3424
3425// let view = cx
3426// .add_window(|cx| {
3427// let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3428// build_editor(buffer, cx)
3429// })
3430// .root(cx);
3431// view.update(cx, |view, cx| {
3432// view.fold_ranges(
3433// vec![
3434// Point::new(0, 2)..Point::new(1, 2),
3435// Point::new(2, 3)..Point::new(4, 1),
3436// Point::new(7, 0)..Point::new(8, 4),
3437// ],
3438// true,
3439// cx,
3440// );
3441// view.change_selections(None, cx, |s| {
3442// s.select_display_ranges([
3443// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3444// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3445// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3446// DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3447// ])
3448// });
3449// assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3450// });
3451
3452// view.update(cx, |view, cx| {
3453// view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3454// assert_eq!(
3455// view.display_text(cx),
3456// "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3457// );
3458// assert_eq!(
3459// view.selections.display_ranges(cx),
3460// [
3461// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3462// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3463// DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3464// DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3465// ]
3466// );
3467// });
3468
3469// view.update(cx, |view, cx| {
3470// view.change_selections(None, cx, |s| {
3471// s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3472// });
3473// view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3474// assert_eq!(
3475// view.display_text(cx),
3476// "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3477// );
3478// assert_eq!(
3479// view.selections.display_ranges(cx),
3480// [
3481// DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3482// DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3483// DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3484// DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3485// DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3486// DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3487// DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3488// DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3489// ]
3490// );
3491// });
3492// }
3493
3494// #[gpui::test]
3495// fn test_add_selection_above_below(cx: &mut TestAppContext) {
3496// init_test(cx, |_| {});
3497
3498// let view = cx
3499// .add_window(|cx| {
3500// let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3501// build_editor(buffer, cx)
3502// })
3503// .root(cx);
3504
3505// view.update(cx, |view, cx| {
3506// view.change_selections(None, cx, |s| {
3507// s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
3508// });
3509// });
3510// view.update(cx, |view, cx| {
3511// view.add_selection_above(&AddSelectionAbove, cx);
3512// assert_eq!(
3513// view.selections.display_ranges(cx),
3514// vec![
3515// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3516// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3517// ]
3518// );
3519// });
3520
3521// view.update(cx, |view, cx| {
3522// view.add_selection_above(&AddSelectionAbove, cx);
3523// assert_eq!(
3524// view.selections.display_ranges(cx),
3525// vec![
3526// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3527// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3528// ]
3529// );
3530// });
3531
3532// view.update(cx, |view, cx| {
3533// view.add_selection_below(&AddSelectionBelow, cx);
3534// assert_eq!(
3535// view.selections.display_ranges(cx),
3536// vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3537// );
3538
3539// view.undo_selection(&UndoSelection, cx);
3540// assert_eq!(
3541// view.selections.display_ranges(cx),
3542// vec![
3543// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3544// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3545// ]
3546// );
3547
3548// view.redo_selection(&RedoSelection, cx);
3549// assert_eq!(
3550// view.selections.display_ranges(cx),
3551// vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3552// );
3553// });
3554
3555// view.update(cx, |view, cx| {
3556// view.add_selection_below(&AddSelectionBelow, cx);
3557// assert_eq!(
3558// view.selections.display_ranges(cx),
3559// vec![
3560// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3561// DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3562// ]
3563// );
3564// });
3565
3566// view.update(cx, |view, cx| {
3567// view.add_selection_below(&AddSelectionBelow, cx);
3568// assert_eq!(
3569// view.selections.display_ranges(cx),
3570// vec![
3571// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3572// DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3573// ]
3574// );
3575// });
3576
3577// view.update(cx, |view, cx| {
3578// view.change_selections(None, cx, |s| {
3579// s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3580// });
3581// });
3582// view.update(cx, |view, cx| {
3583// view.add_selection_below(&AddSelectionBelow, cx);
3584// assert_eq!(
3585// view.selections.display_ranges(cx),
3586// vec![
3587// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3588// DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3589// ]
3590// );
3591// });
3592
3593// view.update(cx, |view, cx| {
3594// view.add_selection_below(&AddSelectionBelow, cx);
3595// assert_eq!(
3596// view.selections.display_ranges(cx),
3597// vec![
3598// DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3599// DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3600// ]
3601// );
3602// });
3603
3604// view.update(cx, |view, cx| {
3605// view.add_selection_above(&AddSelectionAbove, cx);
3606// assert_eq!(
3607// view.selections.display_ranges(cx),
3608// vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3609// );
3610// });
3611
3612// view.update(cx, |view, cx| {
3613// view.add_selection_above(&AddSelectionAbove, cx);
3614// assert_eq!(
3615// view.selections.display_ranges(cx),
3616// vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3617// );
3618// });
3619
3620// view.update(cx, |view, cx| {
3621// view.change_selections(None, cx, |s| {
3622// s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3623// });
3624// view.add_selection_below(&AddSelectionBelow, cx);
3625// assert_eq!(
3626// view.selections.display_ranges(cx),
3627// vec![
3628// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3629// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3630// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3631// ]
3632// );
3633// });
3634
3635// view.update(cx, |view, cx| {
3636// view.add_selection_below(&AddSelectionBelow, cx);
3637// assert_eq!(
3638// view.selections.display_ranges(cx),
3639// vec![
3640// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3641// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3642// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3643// DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3644// ]
3645// );
3646// });
3647
3648// view.update(cx, |view, cx| {
3649// view.add_selection_above(&AddSelectionAbove, cx);
3650// assert_eq!(
3651// view.selections.display_ranges(cx),
3652// vec![
3653// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3654// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3655// DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3656// ]
3657// );
3658// });
3659
3660// view.update(cx, |view, cx| {
3661// view.change_selections(None, cx, |s| {
3662// s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3663// });
3664// });
3665// view.update(cx, |view, cx| {
3666// view.add_selection_above(&AddSelectionAbove, cx);
3667// assert_eq!(
3668// view.selections.display_ranges(cx),
3669// vec![
3670// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3671// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3672// DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3673// DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3674// ]
3675// );
3676// });
3677
3678// view.update(cx, |view, cx| {
3679// view.add_selection_below(&AddSelectionBelow, cx);
3680// assert_eq!(
3681// view.selections.display_ranges(cx),
3682// vec![
3683// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3684// DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3685// DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3686// ]
3687// );
3688// });
3689// }
3690
3691// #[gpui::test]
3692// async fn test_select_next(cx: &mut gpui::TestAppContext) {
3693// init_test(cx, |_| {});
3694
3695// let mut cx = EditorTestContext::new(cx).await;
3696// cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3697
3698// cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3699// .unwrap();
3700// cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3701
3702// cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3703// .unwrap();
3704// cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3705
3706// cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3707// cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3708
3709// cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3710// cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3711
3712// cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3713// .unwrap();
3714// cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3715
3716// cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3717// .unwrap();
3718// cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3719// }
3720
3721// #[gpui::test]
3722// async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3723// init_test(cx, |_| {});
3724// {
3725// // `Select previous` without a selection (selects wordwise)
3726// let mut cx = EditorTestContext::new(cx).await;
3727// cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3728
3729// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3730// .unwrap();
3731// cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3732
3733// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3734// .unwrap();
3735// cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3736
3737// cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3738// cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3739
3740// cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3741// cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3742
3743// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3744// .unwrap();
3745// cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3746
3747// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3748// .unwrap();
3749// cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3750// }
3751// {
3752// // `Select previous` with a selection
3753// let mut cx = EditorTestContext::new(cx).await;
3754// cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3755
3756// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3757// .unwrap();
3758// cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3759
3760// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3761// .unwrap();
3762// cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3763
3764// cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3765// cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3766
3767// cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3768// cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3769
3770// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3771// .unwrap();
3772// cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3773
3774// cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
3775// .unwrap();
3776// cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3777// }
3778// }
3779
3780// #[gpui::test]
3781// async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3782// init_test(cx, |_| {});
3783
3784// let language = Arc::new(Language::new(
3785// LanguageConfig::default(),
3786// Some(tree_sitter_rust::language()),
3787// ));
3788
3789// let text = r#"
3790// use mod1::mod2::{mod3, mod4};
3791
3792// fn fn_1(param1: bool, param2: &str) {
3793// let var1 = "text";
3794// }
3795// "#
3796// .unindent();
3797
3798// let buffer =
3799// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
3800// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3801// let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
3802// view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3803// .await;
3804
3805// view.update(cx, |view, cx| {
3806// view.change_selections(None, cx, |s| {
3807// s.select_display_ranges([
3808// DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3809// DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3810// DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3811// ]);
3812// });
3813// view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3814// });
3815// assert_eq!(
3816// view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3817// &[
3818// DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3819// DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3820// DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3821// ]
3822// );
3823
3824// view.update(cx, |view, cx| {
3825// view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3826// });
3827// assert_eq!(
3828// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3829// &[
3830// DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3831// DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3832// ]
3833// );
3834
3835// view.update(cx, |view, cx| {
3836// view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3837// });
3838// assert_eq!(
3839// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3840// &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3841// );
3842
3843// // Trying to expand the selected syntax node one more time has no effect.
3844// view.update(cx, |view, cx| {
3845// view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3846// });
3847// assert_eq!(
3848// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3849// &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3850// );
3851
3852// view.update(cx, |view, cx| {
3853// view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3854// });
3855// assert_eq!(
3856// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3857// &[
3858// DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3859// DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3860// ]
3861// );
3862
3863// view.update(cx, |view, cx| {
3864// view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3865// });
3866// assert_eq!(
3867// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3868// &[
3869// DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3870// DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3871// DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3872// ]
3873// );
3874
3875// view.update(cx, |view, cx| {
3876// view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3877// });
3878// assert_eq!(
3879// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3880// &[
3881// DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3882// DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3883// DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3884// ]
3885// );
3886
3887// // Trying to shrink the selected syntax node one more time has no effect.
3888// view.update(cx, |view, cx| {
3889// view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3890// });
3891// assert_eq!(
3892// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3893// &[
3894// DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3895// DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3896// DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3897// ]
3898// );
3899
3900// // Ensure that we keep expanding the selection if the larger selection starts or ends within
3901// // a fold.
3902// view.update(cx, |view, cx| {
3903// view.fold_ranges(
3904// vec![
3905// Point::new(0, 21)..Point::new(0, 24),
3906// Point::new(3, 20)..Point::new(3, 22),
3907// ],
3908// true,
3909// cx,
3910// );
3911// view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3912// });
3913// assert_eq!(
3914// view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3915// &[
3916// DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3917// DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3918// DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3919// ]
3920// );
3921// }
3922
3923// #[gpui::test]
3924// async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3925// init_test(cx, |_| {});
3926
3927// let language = Arc::new(
3928// Language::new(
3929// LanguageConfig {
3930// brackets: BracketPairConfig {
3931// pairs: vec![
3932// BracketPair {
3933// start: "{".to_string(),
3934// end: "}".to_string(),
3935// close: false,
3936// newline: true,
3937// },
3938// BracketPair {
3939// start: "(".to_string(),
3940// end: ")".to_string(),
3941// close: false,
3942// newline: true,
3943// },
3944// ],
3945// ..Default::default()
3946// },
3947// ..Default::default()
3948// },
3949// Some(tree_sitter_rust::language()),
3950// )
3951// .with_indents_query(
3952// r#"
3953// (_ "(" ")" @end) @indent
3954// (_ "{" "}" @end) @indent
3955// "#,
3956// )
3957// .unwrap(),
3958// );
3959
3960// let text = "fn a() {}";
3961
3962// let buffer =
3963// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
3964// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3965// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
3966// editor
3967// .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3968// .await;
3969
3970// editor.update(cx, |editor, cx| {
3971// editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3972// editor.newline(&Newline, cx);
3973// assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
3974// assert_eq!(
3975// editor.selections.ranges(cx),
3976// &[
3977// Point::new(1, 4)..Point::new(1, 4),
3978// Point::new(3, 4)..Point::new(3, 4),
3979// Point::new(5, 0)..Point::new(5, 0)
3980// ]
3981// );
3982// });
3983// }
3984
3985// #[gpui::test]
3986// async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3987// init_test(cx, |_| {});
3988
3989// let mut cx = EditorTestContext::new(cx).await;
3990
3991// let language = Arc::new(Language::new(
3992// LanguageConfig {
3993// brackets: BracketPairConfig {
3994// pairs: vec![
3995// BracketPair {
3996// start: "{".to_string(),
3997// end: "}".to_string(),
3998// close: true,
3999// newline: true,
4000// },
4001// BracketPair {
4002// start: "(".to_string(),
4003// end: ")".to_string(),
4004// close: true,
4005// newline: true,
4006// },
4007// BracketPair {
4008// start: "/*".to_string(),
4009// end: " */".to_string(),
4010// close: true,
4011// newline: true,
4012// },
4013// BracketPair {
4014// start: "[".to_string(),
4015// end: "]".to_string(),
4016// close: false,
4017// newline: true,
4018// },
4019// BracketPair {
4020// start: "\"".to_string(),
4021// end: "\"".to_string(),
4022// close: true,
4023// newline: false,
4024// },
4025// ],
4026// ..Default::default()
4027// },
4028// autoclose_before: "})]".to_string(),
4029// ..Default::default()
4030// },
4031// Some(tree_sitter_rust::language()),
4032// ));
4033
4034// let registry = Arc::new(LanguageRegistry::test());
4035// registry.add(language.clone());
4036// cx.update_buffer(|buffer, cx| {
4037// buffer.set_language_registry(registry);
4038// buffer.set_language(Some(language), cx);
4039// });
4040
4041// cx.set_state(
4042// &r#"
4043// 🏀ˇ
4044// εˇ
4045// ❤️ˇ
4046// "#
4047// .unindent(),
4048// );
4049
4050// // autoclose multiple nested brackets at multiple cursors
4051// cx.update_editor(|view, cx| {
4052// view.handle_input("{", cx);
4053// view.handle_input("{", cx);
4054// view.handle_input("{", cx);
4055// });
4056// cx.assert_editor_state(
4057// &"
4058// 🏀{{{ˇ}}}
4059// ε{{{ˇ}}}
4060// ❤️{{{ˇ}}}
4061// "
4062// .unindent(),
4063// );
4064
4065// // insert a different closing bracket
4066// cx.update_editor(|view, cx| {
4067// view.handle_input(")", cx);
4068// });
4069// cx.assert_editor_state(
4070// &"
4071// 🏀{{{)ˇ}}}
4072// ε{{{)ˇ}}}
4073// ❤️{{{)ˇ}}}
4074// "
4075// .unindent(),
4076// );
4077
4078// // skip over the auto-closed brackets when typing a closing bracket
4079// cx.update_editor(|view, cx| {
4080// view.move_right(&MoveRight, cx);
4081// view.handle_input("}", cx);
4082// view.handle_input("}", cx);
4083// view.handle_input("}", cx);
4084// });
4085// cx.assert_editor_state(
4086// &"
4087// 🏀{{{)}}}}ˇ
4088// ε{{{)}}}}ˇ
4089// ❤️{{{)}}}}ˇ
4090// "
4091// .unindent(),
4092// );
4093
4094// // autoclose multi-character pairs
4095// cx.set_state(
4096// &"
4097// ˇ
4098// ˇ
4099// "
4100// .unindent(),
4101// );
4102// cx.update_editor(|view, cx| {
4103// view.handle_input("/", cx);
4104// view.handle_input("*", cx);
4105// });
4106// cx.assert_editor_state(
4107// &"
4108// /*ˇ */
4109// /*ˇ */
4110// "
4111// .unindent(),
4112// );
4113
4114// // one cursor autocloses a multi-character pair, one cursor
4115// // does not autoclose.
4116// cx.set_state(
4117// &"
4118// /ˇ
4119// ˇ
4120// "
4121// .unindent(),
4122// );
4123// cx.update_editor(|view, cx| view.handle_input("*", cx));
4124// cx.assert_editor_state(
4125// &"
4126// /*ˇ */
4127// *ˇ
4128// "
4129// .unindent(),
4130// );
4131
4132// // Don't autoclose if the next character isn't whitespace and isn't
4133// // listed in the language's "autoclose_before" section.
4134// cx.set_state("ˇa b");
4135// cx.update_editor(|view, cx| view.handle_input("{", cx));
4136// cx.assert_editor_state("{ˇa b");
4137
4138// // Don't autoclose if `close` is false for the bracket pair
4139// cx.set_state("ˇ");
4140// cx.update_editor(|view, cx| view.handle_input("[", cx));
4141// cx.assert_editor_state("[ˇ");
4142
4143// // Surround with brackets if text is selected
4144// cx.set_state("«aˇ» b");
4145// cx.update_editor(|view, cx| view.handle_input("{", cx));
4146// cx.assert_editor_state("{«aˇ»} b");
4147
4148// // Autclose pair where the start and end characters are the same
4149// cx.set_state("aˇ");
4150// cx.update_editor(|view, cx| view.handle_input("\"", cx));
4151// cx.assert_editor_state("a\"ˇ\"");
4152// cx.update_editor(|view, cx| view.handle_input("\"", cx));
4153// cx.assert_editor_state("a\"\"ˇ");
4154// }
4155
4156// #[gpui::test]
4157// async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4158// init_test(cx, |_| {});
4159
4160// let mut cx = EditorTestContext::new(cx).await;
4161
4162// let html_language = Arc::new(
4163// Language::new(
4164// LanguageConfig {
4165// name: "HTML".into(),
4166// brackets: BracketPairConfig {
4167// pairs: vec![
4168// BracketPair {
4169// start: "<".into(),
4170// end: ">".into(),
4171// close: true,
4172// ..Default::default()
4173// },
4174// BracketPair {
4175// start: "{".into(),
4176// end: "}".into(),
4177// close: true,
4178// ..Default::default()
4179// },
4180// BracketPair {
4181// start: "(".into(),
4182// end: ")".into(),
4183// close: true,
4184// ..Default::default()
4185// },
4186// ],
4187// ..Default::default()
4188// },
4189// autoclose_before: "})]>".into(),
4190// ..Default::default()
4191// },
4192// Some(tree_sitter_html::language()),
4193// )
4194// .with_injection_query(
4195// r#"
4196// (script_element
4197// (raw_text) @content
4198// (#set! "language" "javascript"))
4199// "#,
4200// )
4201// .unwrap(),
4202// );
4203
4204// let javascript_language = Arc::new(Language::new(
4205// LanguageConfig {
4206// name: "JavaScript".into(),
4207// brackets: BracketPairConfig {
4208// pairs: vec![
4209// BracketPair {
4210// start: "/*".into(),
4211// end: " */".into(),
4212// close: true,
4213// ..Default::default()
4214// },
4215// BracketPair {
4216// start: "{".into(),
4217// end: "}".into(),
4218// close: true,
4219// ..Default::default()
4220// },
4221// BracketPair {
4222// start: "(".into(),
4223// end: ")".into(),
4224// close: true,
4225// ..Default::default()
4226// },
4227// ],
4228// ..Default::default()
4229// },
4230// autoclose_before: "})]>".into(),
4231// ..Default::default()
4232// },
4233// Some(tree_sitter_typescript::language_tsx()),
4234// ));
4235
4236// let registry = Arc::new(LanguageRegistry::test());
4237// registry.add(html_language.clone());
4238// registry.add(javascript_language.clone());
4239
4240// cx.update_buffer(|buffer, cx| {
4241// buffer.set_language_registry(registry);
4242// buffer.set_language(Some(html_language), cx);
4243// });
4244
4245// cx.set_state(
4246// &r#"
4247// <body>ˇ
4248// <script>
4249// var x = 1;ˇ
4250// </script>
4251// </body>ˇ
4252// "#
4253// .unindent(),
4254// );
4255
4256// // Precondition: different languages are active at different locations.
4257// cx.update_editor(|editor, cx| {
4258// let snapshot = editor.snapshot(cx);
4259// let cursors = editor.selections.ranges::<usize>(cx);
4260// let languages = cursors
4261// .iter()
4262// .map(|c| snapshot.language_at(c.start).unwrap().name())
4263// .collect::<Vec<_>>();
4264// assert_eq!(
4265// languages,
4266// &["HTML".into(), "JavaScript".into(), "HTML".into()]
4267// );
4268// });
4269
4270// // Angle brackets autoclose in HTML, but not JavaScript.
4271// cx.update_editor(|editor, cx| {
4272// editor.handle_input("<", cx);
4273// editor.handle_input("a", cx);
4274// });
4275// cx.assert_editor_state(
4276// &r#"
4277// <body><aˇ>
4278// <script>
4279// var x = 1;<aˇ
4280// </script>
4281// </body><aˇ>
4282// "#
4283// .unindent(),
4284// );
4285
4286// // Curly braces and parens autoclose in both HTML and JavaScript.
4287// cx.update_editor(|editor, cx| {
4288// editor.handle_input(" b=", cx);
4289// editor.handle_input("{", cx);
4290// editor.handle_input("c", cx);
4291// editor.handle_input("(", cx);
4292// });
4293// cx.assert_editor_state(
4294// &r#"
4295// <body><a b={c(ˇ)}>
4296// <script>
4297// var x = 1;<a b={c(ˇ)}
4298// </script>
4299// </body><a b={c(ˇ)}>
4300// "#
4301// .unindent(),
4302// );
4303
4304// // Brackets that were already autoclosed are skipped.
4305// cx.update_editor(|editor, cx| {
4306// editor.handle_input(")", cx);
4307// editor.handle_input("d", cx);
4308// editor.handle_input("}", cx);
4309// });
4310// cx.assert_editor_state(
4311// &r#"
4312// <body><a b={c()d}ˇ>
4313// <script>
4314// var x = 1;<a b={c()d}ˇ
4315// </script>
4316// </body><a b={c()d}ˇ>
4317// "#
4318// .unindent(),
4319// );
4320// cx.update_editor(|editor, cx| {
4321// editor.handle_input(">", cx);
4322// });
4323// cx.assert_editor_state(
4324// &r#"
4325// <body><a b={c()d}>ˇ
4326// <script>
4327// var x = 1;<a b={c()d}>ˇ
4328// </script>
4329// </body><a b={c()d}>ˇ
4330// "#
4331// .unindent(),
4332// );
4333
4334// // Reset
4335// cx.set_state(
4336// &r#"
4337// <body>ˇ
4338// <script>
4339// var x = 1;ˇ
4340// </script>
4341// </body>ˇ
4342// "#
4343// .unindent(),
4344// );
4345
4346// cx.update_editor(|editor, cx| {
4347// editor.handle_input("<", cx);
4348// });
4349// cx.assert_editor_state(
4350// &r#"
4351// <body><ˇ>
4352// <script>
4353// var x = 1;<ˇ
4354// </script>
4355// </body><ˇ>
4356// "#
4357// .unindent(),
4358// );
4359
4360// // When backspacing, the closing angle brackets are removed.
4361// cx.update_editor(|editor, cx| {
4362// editor.backspace(&Backspace, cx);
4363// });
4364// cx.assert_editor_state(
4365// &r#"
4366// <body>ˇ
4367// <script>
4368// var x = 1;ˇ
4369// </script>
4370// </body>ˇ
4371// "#
4372// .unindent(),
4373// );
4374
4375// // Block comments autoclose in JavaScript, but not HTML.
4376// cx.update_editor(|editor, cx| {
4377// editor.handle_input("/", cx);
4378// editor.handle_input("*", cx);
4379// });
4380// cx.assert_editor_state(
4381// &r#"
4382// <body>/*ˇ
4383// <script>
4384// var x = 1;/*ˇ */
4385// </script>
4386// </body>/*ˇ
4387// "#
4388// .unindent(),
4389// );
4390// }
4391
4392// #[gpui::test]
4393// async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4394// init_test(cx, |_| {});
4395
4396// let mut cx = EditorTestContext::new(cx).await;
4397
4398// let rust_language = Arc::new(
4399// Language::new(
4400// LanguageConfig {
4401// name: "Rust".into(),
4402// brackets: serde_json::from_value(json!([
4403// { "start": "{", "end": "}", "close": true, "newline": true },
4404// { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4405// ]))
4406// .unwrap(),
4407// autoclose_before: "})]>".into(),
4408// ..Default::default()
4409// },
4410// Some(tree_sitter_rust::language()),
4411// )
4412// .with_override_query("(string_literal) @string")
4413// .unwrap(),
4414// );
4415
4416// let registry = Arc::new(LanguageRegistry::test());
4417// registry.add(rust_language.clone());
4418
4419// cx.update_buffer(|buffer, cx| {
4420// buffer.set_language_registry(registry);
4421// buffer.set_language(Some(rust_language), cx);
4422// });
4423
4424// cx.set_state(
4425// &r#"
4426// let x = ˇ
4427// "#
4428// .unindent(),
4429// );
4430
4431// // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4432// cx.update_editor(|editor, cx| {
4433// editor.handle_input("\"", cx);
4434// });
4435// cx.assert_editor_state(
4436// &r#"
4437// let x = "ˇ"
4438// "#
4439// .unindent(),
4440// );
4441
4442// // Inserting another quotation mark. The cursor moves across the existing
4443// // automatically-inserted quotation mark.
4444// cx.update_editor(|editor, cx| {
4445// editor.handle_input("\"", cx);
4446// });
4447// cx.assert_editor_state(
4448// &r#"
4449// let x = ""ˇ
4450// "#
4451// .unindent(),
4452// );
4453
4454// // Reset
4455// cx.set_state(
4456// &r#"
4457// let x = ˇ
4458// "#
4459// .unindent(),
4460// );
4461
4462// // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4463// cx.update_editor(|editor, cx| {
4464// editor.handle_input("\"", cx);
4465// editor.handle_input(" ", cx);
4466// editor.move_left(&Default::default(), cx);
4467// editor.handle_input("\\", cx);
4468// editor.handle_input("\"", cx);
4469// });
4470// cx.assert_editor_state(
4471// &r#"
4472// let x = "\"ˇ "
4473// "#
4474// .unindent(),
4475// );
4476
4477// // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4478// // mark. Nothing is inserted.
4479// cx.update_editor(|editor, cx| {
4480// editor.move_right(&Default::default(), cx);
4481// editor.handle_input("\"", cx);
4482// });
4483// cx.assert_editor_state(
4484// &r#"
4485// let x = "\" "ˇ
4486// "#
4487// .unindent(),
4488// );
4489// }
4490
4491// #[gpui::test]
4492// async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4493// init_test(cx, |_| {});
4494
4495// let language = Arc::new(Language::new(
4496// LanguageConfig {
4497// brackets: BracketPairConfig {
4498// pairs: vec![
4499// BracketPair {
4500// start: "{".to_string(),
4501// end: "}".to_string(),
4502// close: true,
4503// newline: true,
4504// },
4505// BracketPair {
4506// start: "/* ".to_string(),
4507// end: "*/".to_string(),
4508// close: true,
4509// ..Default::default()
4510// },
4511// ],
4512// ..Default::default()
4513// },
4514// ..Default::default()
4515// },
4516// Some(tree_sitter_rust::language()),
4517// ));
4518
4519// let text = r#"
4520// a
4521// b
4522// c
4523// "#
4524// .unindent();
4525
4526// let buffer =
4527// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
4528// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4529// let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4530// view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4531// .await;
4532
4533// view.update(cx, |view, cx| {
4534// view.change_selections(None, cx, |s| {
4535// s.select_display_ranges([
4536// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4537// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4538// DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4539// ])
4540// });
4541
4542// view.handle_input("{", cx);
4543// view.handle_input("{", cx);
4544// view.handle_input("{", cx);
4545// assert_eq!(
4546// view.text(cx),
4547// "
4548// {{{a}}}
4549// {{{b}}}
4550// {{{c}}}
4551// "
4552// .unindent()
4553// );
4554// assert_eq!(
4555// view.selections.display_ranges(cx),
4556// [
4557// DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4558// DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4559// DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4560// ]
4561// );
4562
4563// view.undo(&Undo, cx);
4564// view.undo(&Undo, cx);
4565// view.undo(&Undo, cx);
4566// assert_eq!(
4567// view.text(cx),
4568// "
4569// a
4570// b
4571// c
4572// "
4573// .unindent()
4574// );
4575// assert_eq!(
4576// view.selections.display_ranges(cx),
4577// [
4578// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4579// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4580// DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4581// ]
4582// );
4583
4584// // Ensure inserting the first character of a multi-byte bracket pair
4585// // doesn't surround the selections with the bracket.
4586// view.handle_input("/", cx);
4587// assert_eq!(
4588// view.text(cx),
4589// "
4590// /
4591// /
4592// /
4593// "
4594// .unindent()
4595// );
4596// assert_eq!(
4597// view.selections.display_ranges(cx),
4598// [
4599// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4600// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4601// DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4602// ]
4603// );
4604
4605// view.undo(&Undo, cx);
4606// assert_eq!(
4607// view.text(cx),
4608// "
4609// a
4610// b
4611// c
4612// "
4613// .unindent()
4614// );
4615// assert_eq!(
4616// view.selections.display_ranges(cx),
4617// [
4618// DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4619// DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4620// DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4621// ]
4622// );
4623
4624// // Ensure inserting the last character of a multi-byte bracket pair
4625// // doesn't surround the selections with the bracket.
4626// view.handle_input("*", cx);
4627// assert_eq!(
4628// view.text(cx),
4629// "
4630// *
4631// *
4632// *
4633// "
4634// .unindent()
4635// );
4636// assert_eq!(
4637// view.selections.display_ranges(cx),
4638// [
4639// DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4640// DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4641// DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4642// ]
4643// );
4644// });
4645// }
4646
4647// #[gpui::test]
4648// async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4649// init_test(cx, |_| {});
4650
4651// let language = Arc::new(Language::new(
4652// LanguageConfig {
4653// brackets: BracketPairConfig {
4654// pairs: vec![BracketPair {
4655// start: "{".to_string(),
4656// end: "}".to_string(),
4657// close: true,
4658// newline: true,
4659// }],
4660// ..Default::default()
4661// },
4662// autoclose_before: "}".to_string(),
4663// ..Default::default()
4664// },
4665// Some(tree_sitter_rust::language()),
4666// ));
4667
4668// let text = r#"
4669// a
4670// b
4671// c
4672// "#
4673// .unindent();
4674
4675// let buffer =
4676// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
4677// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4678// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4679// editor
4680// .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4681// .await;
4682
4683// editor.update(cx, |editor, cx| {
4684// editor.change_selections(None, cx, |s| {
4685// s.select_ranges([
4686// Point::new(0, 1)..Point::new(0, 1),
4687// Point::new(1, 1)..Point::new(1, 1),
4688// Point::new(2, 1)..Point::new(2, 1),
4689// ])
4690// });
4691
4692// editor.handle_input("{", cx);
4693// editor.handle_input("{", cx);
4694// editor.handle_input("_", cx);
4695// assert_eq!(
4696// editor.text(cx),
4697// "
4698// a{{_}}
4699// b{{_}}
4700// c{{_}}
4701// "
4702// .unindent()
4703// );
4704// assert_eq!(
4705// editor.selections.ranges::<Point>(cx),
4706// [
4707// Point::new(0, 4)..Point::new(0, 4),
4708// Point::new(1, 4)..Point::new(1, 4),
4709// Point::new(2, 4)..Point::new(2, 4)
4710// ]
4711// );
4712
4713// editor.backspace(&Default::default(), cx);
4714// editor.backspace(&Default::default(), cx);
4715// assert_eq!(
4716// editor.text(cx),
4717// "
4718// a{}
4719// b{}
4720// c{}
4721// "
4722// .unindent()
4723// );
4724// assert_eq!(
4725// editor.selections.ranges::<Point>(cx),
4726// [
4727// Point::new(0, 2)..Point::new(0, 2),
4728// Point::new(1, 2)..Point::new(1, 2),
4729// Point::new(2, 2)..Point::new(2, 2)
4730// ]
4731// );
4732
4733// editor.delete_to_previous_word_start(&Default::default(), cx);
4734// assert_eq!(
4735// editor.text(cx),
4736// "
4737// a
4738// b
4739// c
4740// "
4741// .unindent()
4742// );
4743// assert_eq!(
4744// editor.selections.ranges::<Point>(cx),
4745// [
4746// Point::new(0, 1)..Point::new(0, 1),
4747// Point::new(1, 1)..Point::new(1, 1),
4748// Point::new(2, 1)..Point::new(2, 1)
4749// ]
4750// );
4751// });
4752// }
4753
4754// #[gpui::test]
4755// async fn test_snippets(cx: &mut gpui::TestAppContext) {
4756// init_test(cx, |_| {});
4757
4758// let (text, insertion_ranges) = marked_text_ranges(
4759// indoc! {"
4760// a.ˇ b
4761// a.ˇ b
4762// a.ˇ b
4763// "},
4764// false,
4765// );
4766
4767// let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4768// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4769
4770// editor.update(cx, |editor, cx| {
4771// let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4772
4773// editor
4774// .insert_snippet(&insertion_ranges, snippet, cx)
4775// .unwrap();
4776
4777// fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4778// let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4779// assert_eq!(editor.text(cx), expected_text);
4780// assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4781// }
4782
4783// assert(
4784// editor,
4785// cx,
4786// indoc! {"
4787// a.f(«one», two, «three») b
4788// a.f(«one», two, «three») b
4789// a.f(«one», two, «three») b
4790// "},
4791// );
4792
4793// // Can't move earlier than the first tab stop
4794// assert!(!editor.move_to_prev_snippet_tabstop(cx));
4795// assert(
4796// editor,
4797// cx,
4798// indoc! {"
4799// a.f(«one», two, «three») b
4800// a.f(«one», two, «three») b
4801// a.f(«one», two, «three») b
4802// "},
4803// );
4804
4805// assert!(editor.move_to_next_snippet_tabstop(cx));
4806// assert(
4807// editor,
4808// cx,
4809// indoc! {"
4810// a.f(one, «two», three) b
4811// a.f(one, «two», three) b
4812// a.f(one, «two», three) b
4813// "},
4814// );
4815
4816// editor.move_to_prev_snippet_tabstop(cx);
4817// assert(
4818// editor,
4819// cx,
4820// indoc! {"
4821// a.f(«one», two, «three») b
4822// a.f(«one», two, «three») b
4823// a.f(«one», two, «three») b
4824// "},
4825// );
4826
4827// assert!(editor.move_to_next_snippet_tabstop(cx));
4828// assert(
4829// editor,
4830// cx,
4831// indoc! {"
4832// a.f(one, «two», three) b
4833// a.f(one, «two», three) b
4834// a.f(one, «two», three) b
4835// "},
4836// );
4837// assert!(editor.move_to_next_snippet_tabstop(cx));
4838// assert(
4839// editor,
4840// cx,
4841// indoc! {"
4842// a.f(one, two, three)ˇ b
4843// a.f(one, two, three)ˇ b
4844// a.f(one, two, three)ˇ b
4845// "},
4846// );
4847
4848// // As soon as the last tab stop is reached, snippet state is gone
4849// editor.move_to_prev_snippet_tabstop(cx);
4850// assert(
4851// editor,
4852// cx,
4853// indoc! {"
4854// a.f(one, two, three)ˇ b
4855// a.f(one, two, three)ˇ b
4856// a.f(one, two, three)ˇ b
4857// "},
4858// );
4859// });
4860// }
4861
4862// #[gpui::test]
4863// async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4864// init_test(cx, |_| {});
4865
4866// let mut language = Language::new(
4867// LanguageConfig {
4868// name: "Rust".into(),
4869// path_suffixes: vec!["rs".to_string()],
4870// ..Default::default()
4871// },
4872// Some(tree_sitter_rust::language()),
4873// );
4874// let mut fake_servers = language
4875// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4876// capabilities: lsp::ServerCapabilities {
4877// document_formatting_provider: Some(lsp::OneOf::Left(true)),
4878// ..Default::default()
4879// },
4880// ..Default::default()
4881// }))
4882// .await;
4883
4884// let fs = FakeFs::new(cx.background());
4885// fs.insert_file("/file.rs", Default::default()).await;
4886
4887// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4888// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4889// let buffer = project
4890// .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4891// .await
4892// .unwrap();
4893
4894// cx.foreground().start_waiting();
4895// let fake_server = fake_servers.next().await.unwrap();
4896
4897// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4898// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4899// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4900// assert!(cx.read(|cx| editor.is_dirty(cx)));
4901
4902// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4903// fake_server
4904// .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4905// assert_eq!(
4906// params.text_document.uri,
4907// lsp::Url::from_file_path("/file.rs").unwrap()
4908// );
4909// assert_eq!(params.options.tab_size, 4);
4910// Ok(Some(vec![lsp::TextEdit::new(
4911// lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4912// ", ".to_string(),
4913// )]))
4914// })
4915// .next()
4916// .await;
4917// cx.foreground().start_waiting();
4918// save.await.unwrap();
4919// assert_eq!(
4920// editor.read_with(cx, |editor, cx| editor.text(cx)),
4921// "one, two\nthree\n"
4922// );
4923// assert!(!cx.read(|cx| editor.is_dirty(cx)));
4924
4925// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4926// assert!(cx.read(|cx| editor.is_dirty(cx)));
4927
4928// // Ensure we can still save even if formatting hangs.
4929// fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4930// assert_eq!(
4931// params.text_document.uri,
4932// lsp::Url::from_file_path("/file.rs").unwrap()
4933// );
4934// futures::future::pending::<()>().await;
4935// unreachable!()
4936// });
4937// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4938// cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4939// cx.foreground().start_waiting();
4940// save.await.unwrap();
4941// assert_eq!(
4942// editor.read_with(cx, |editor, cx| editor.text(cx)),
4943// "one\ntwo\nthree\n"
4944// );
4945// assert!(!cx.read(|cx| editor.is_dirty(cx)));
4946
4947// // Set rust language override and assert overridden tabsize is sent to language server
4948// update_test_language_settings(cx, |settings| {
4949// settings.languages.insert(
4950// "Rust".into(),
4951// LanguageSettingsContent {
4952// tab_size: NonZeroU32::new(8),
4953// ..Default::default()
4954// },
4955// );
4956// });
4957
4958// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4959// fake_server
4960// .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4961// assert_eq!(
4962// params.text_document.uri,
4963// lsp::Url::from_file_path("/file.rs").unwrap()
4964// );
4965// assert_eq!(params.options.tab_size, 8);
4966// Ok(Some(vec![]))
4967// })
4968// .next()
4969// .await;
4970// cx.foreground().start_waiting();
4971// save.await.unwrap();
4972// }
4973
4974// #[gpui::test]
4975// async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4976// init_test(cx, |_| {});
4977
4978// let mut language = Language::new(
4979// LanguageConfig {
4980// name: "Rust".into(),
4981// path_suffixes: vec!["rs".to_string()],
4982// ..Default::default()
4983// },
4984// Some(tree_sitter_rust::language()),
4985// );
4986// let mut fake_servers = language
4987// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4988// capabilities: lsp::ServerCapabilities {
4989// document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4990// ..Default::default()
4991// },
4992// ..Default::default()
4993// }))
4994// .await;
4995
4996// let fs = FakeFs::new(cx.background());
4997// fs.insert_file("/file.rs", Default::default()).await;
4998
4999// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5000// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
5001// let buffer = project
5002// .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5003// .await
5004// .unwrap();
5005
5006// cx.foreground().start_waiting();
5007// let fake_server = fake_servers.next().await.unwrap();
5008
5009// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5010// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
5011// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5012// assert!(cx.read(|cx| editor.is_dirty(cx)));
5013
5014// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
5015// fake_server
5016// .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5017// assert_eq!(
5018// params.text_document.uri,
5019// lsp::Url::from_file_path("/file.rs").unwrap()
5020// );
5021// assert_eq!(params.options.tab_size, 4);
5022// Ok(Some(vec![lsp::TextEdit::new(
5023// lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5024// ", ".to_string(),
5025// )]))
5026// })
5027// .next()
5028// .await;
5029// cx.foreground().start_waiting();
5030// save.await.unwrap();
5031// assert_eq!(
5032// editor.read_with(cx, |editor, cx| editor.text(cx)),
5033// "one, two\nthree\n"
5034// );
5035// assert!(!cx.read(|cx| editor.is_dirty(cx)));
5036
5037// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5038// assert!(cx.read(|cx| editor.is_dirty(cx)));
5039
5040// // Ensure we can still save even if formatting hangs.
5041// fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5042// move |params, _| async move {
5043// assert_eq!(
5044// params.text_document.uri,
5045// lsp::Url::from_file_path("/file.rs").unwrap()
5046// );
5047// futures::future::pending::<()>().await;
5048// unreachable!()
5049// },
5050// );
5051// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
5052// cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
5053// cx.foreground().start_waiting();
5054// save.await.unwrap();
5055// assert_eq!(
5056// editor.read_with(cx, |editor, cx| editor.text(cx)),
5057// "one\ntwo\nthree\n"
5058// );
5059// assert!(!cx.read(|cx| editor.is_dirty(cx)));
5060
5061// // Set rust language override and assert overridden tabsize is sent to language server
5062// update_test_language_settings(cx, |settings| {
5063// settings.languages.insert(
5064// "Rust".into(),
5065// LanguageSettingsContent {
5066// tab_size: NonZeroU32::new(8),
5067// ..Default::default()
5068// },
5069// );
5070// });
5071
5072// let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
5073// fake_server
5074// .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5075// assert_eq!(
5076// params.text_document.uri,
5077// lsp::Url::from_file_path("/file.rs").unwrap()
5078// );
5079// assert_eq!(params.options.tab_size, 8);
5080// Ok(Some(vec![]))
5081// })
5082// .next()
5083// .await;
5084// cx.foreground().start_waiting();
5085// save.await.unwrap();
5086// }
5087
5088// #[gpui::test]
5089// async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5090// init_test(cx, |settings| {
5091// settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5092// });
5093
5094// let mut language = Language::new(
5095// LanguageConfig {
5096// name: "Rust".into(),
5097// path_suffixes: vec!["rs".to_string()],
5098// // Enable Prettier formatting for the same buffer, and ensure
5099// // LSP is called instead of Prettier.
5100// prettier_parser_name: Some("test_parser".to_string()),
5101// ..Default::default()
5102// },
5103// Some(tree_sitter_rust::language()),
5104// );
5105// let mut fake_servers = language
5106// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5107// capabilities: lsp::ServerCapabilities {
5108// document_formatting_provider: Some(lsp::OneOf::Left(true)),
5109// ..Default::default()
5110// },
5111// ..Default::default()
5112// }))
5113// .await;
5114
5115// let fs = FakeFs::new(cx.background());
5116// fs.insert_file("/file.rs", Default::default()).await;
5117
5118// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5119// project.update(cx, |project, _| {
5120// project.languages().add(Arc::new(language));
5121// });
5122// let buffer = project
5123// .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5124// .await
5125// .unwrap();
5126
5127// cx.foreground().start_waiting();
5128// let fake_server = fake_servers.next().await.unwrap();
5129
5130// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5131// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
5132// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5133
5134// let format = editor.update(cx, |editor, cx| {
5135// editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5136// });
5137// fake_server
5138// .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5139// assert_eq!(
5140// params.text_document.uri,
5141// lsp::Url::from_file_path("/file.rs").unwrap()
5142// );
5143// assert_eq!(params.options.tab_size, 4);
5144// Ok(Some(vec![lsp::TextEdit::new(
5145// lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5146// ", ".to_string(),
5147// )]))
5148// })
5149// .next()
5150// .await;
5151// cx.foreground().start_waiting();
5152// format.await.unwrap();
5153// assert_eq!(
5154// editor.read_with(cx, |editor, cx| editor.text(cx)),
5155// "one, two\nthree\n"
5156// );
5157
5158// editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5159// // Ensure we don't lock if formatting hangs.
5160// fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5161// assert_eq!(
5162// params.text_document.uri,
5163// lsp::Url::from_file_path("/file.rs").unwrap()
5164// );
5165// futures::future::pending::<()>().await;
5166// unreachable!()
5167// });
5168// let format = editor.update(cx, |editor, cx| {
5169// editor.perform_format(project, FormatTrigger::Manual, cx)
5170// });
5171// cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
5172// cx.foreground().start_waiting();
5173// format.await.unwrap();
5174// assert_eq!(
5175// editor.read_with(cx, |editor, cx| editor.text(cx)),
5176// "one\ntwo\nthree\n"
5177// );
5178// }
5179
5180// #[gpui::test]
5181// async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5182// init_test(cx, |_| {});
5183
5184// let mut cx = EditorLspTestContext::new_rust(
5185// lsp::ServerCapabilities {
5186// document_formatting_provider: Some(lsp::OneOf::Left(true)),
5187// ..Default::default()
5188// },
5189// cx,
5190// )
5191// .await;
5192
5193// cx.set_state(indoc! {"
5194// one.twoˇ
5195// "});
5196
5197// // The format request takes a long time. When it completes, it inserts
5198// // a newline and an indent before the `.`
5199// cx.lsp
5200// .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5201// let executor = cx.background();
5202// async move {
5203// executor.timer(Duration::from_millis(100)).await;
5204// Ok(Some(vec![lsp::TextEdit {
5205// range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5206// new_text: "\n ".into(),
5207// }]))
5208// }
5209// });
5210
5211// // Submit a format request.
5212// let format_1 = cx
5213// .update_editor(|editor, cx| editor.format(&Format, cx))
5214// .unwrap();
5215// cx.foreground().run_until_parked();
5216
5217// // Submit a second format request.
5218// let format_2 = cx
5219// .update_editor(|editor, cx| editor.format(&Format, cx))
5220// .unwrap();
5221// cx.foreground().run_until_parked();
5222
5223// // Wait for both format requests to complete
5224// cx.foreground().advance_clock(Duration::from_millis(200));
5225// cx.foreground().start_waiting();
5226// format_1.await.unwrap();
5227// cx.foreground().start_waiting();
5228// format_2.await.unwrap();
5229
5230// // The formatting edits only happens once.
5231// cx.assert_editor_state(indoc! {"
5232// one
5233// .twoˇ
5234// "});
5235// }
5236
5237// #[gpui::test]
5238// async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5239// init_test(cx, |settings| {
5240// settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5241// });
5242
5243// let mut cx = EditorLspTestContext::new_rust(
5244// lsp::ServerCapabilities {
5245// document_formatting_provider: Some(lsp::OneOf::Left(true)),
5246// ..Default::default()
5247// },
5248// cx,
5249// )
5250// .await;
5251
5252// // Set up a buffer white some trailing whitespace and no trailing newline.
5253// cx.set_state(
5254// &[
5255// "one ", //
5256// "twoˇ", //
5257// "three ", //
5258// "four", //
5259// ]
5260// .join("\n"),
5261// );
5262
5263// // Submit a format request.
5264// let format = cx
5265// .update_editor(|editor, cx| editor.format(&Format, cx))
5266// .unwrap();
5267
5268// // Record which buffer changes have been sent to the language server
5269// let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5270// cx.lsp
5271// .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5272// let buffer_changes = buffer_changes.clone();
5273// move |params, _| {
5274// buffer_changes.lock().extend(
5275// params
5276// .content_changes
5277// .into_iter()
5278// .map(|e| (e.range.unwrap(), e.text)),
5279// );
5280// }
5281// });
5282
5283// // Handle formatting requests to the language server.
5284// cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5285// let buffer_changes = buffer_changes.clone();
5286// move |_, _| {
5287// // When formatting is requested, trailing whitespace has already been stripped,
5288// // and the trailing newline has already been added.
5289// assert_eq!(
5290// &buffer_changes.lock()[1..],
5291// &[
5292// (
5293// lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5294// "".into()
5295// ),
5296// (
5297// lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5298// "".into()
5299// ),
5300// (
5301// lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5302// "\n".into()
5303// ),
5304// ]
5305// );
5306
5307// // Insert blank lines between each line of the buffer.
5308// async move {
5309// Ok(Some(vec![
5310// lsp::TextEdit {
5311// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5312// new_text: "\n".into(),
5313// },
5314// lsp::TextEdit {
5315// range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5316// new_text: "\n".into(),
5317// },
5318// ]))
5319// }
5320// }
5321// });
5322
5323// // After formatting the buffer, the trailing whitespace is stripped,
5324// // a newline is appended, and the edits provided by the language server
5325// // have been applied.
5326// format.await.unwrap();
5327// cx.assert_editor_state(
5328// &[
5329// "one", //
5330// "", //
5331// "twoˇ", //
5332// "", //
5333// "three", //
5334// "four", //
5335// "", //
5336// ]
5337// .join("\n"),
5338// );
5339
5340// // Undoing the formatting undoes the trailing whitespace removal, the
5341// // trailing newline, and the LSP edits.
5342// cx.update_buffer(|buffer, cx| buffer.undo(cx));
5343// cx.assert_editor_state(
5344// &[
5345// "one ", //
5346// "twoˇ", //
5347// "three ", //
5348// "four", //
5349// ]
5350// .join("\n"),
5351// );
5352// }
5353
5354// #[gpui::test]
5355// async fn test_completion(cx: &mut gpui::TestAppContext) {
5356// init_test(cx, |_| {});
5357
5358// let mut cx = EditorLspTestContext::new_rust(
5359// lsp::ServerCapabilities {
5360// completion_provider: Some(lsp::CompletionOptions {
5361// trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5362// resolve_provider: Some(true),
5363// ..Default::default()
5364// }),
5365// ..Default::default()
5366// },
5367// cx,
5368// )
5369// .await;
5370
5371// cx.set_state(indoc! {"
5372// oneˇ
5373// two
5374// three
5375// "});
5376// cx.simulate_keystroke(".");
5377// handle_completion_request(
5378// &mut cx,
5379// indoc! {"
5380// one.|<>
5381// two
5382// three
5383// "},
5384// vec!["first_completion", "second_completion"],
5385// )
5386// .await;
5387// cx.condition(|editor, _| editor.context_menu_visible())
5388// .await;
5389// let apply_additional_edits = cx.update_editor(|editor, cx| {
5390// editor.context_menu_next(&Default::default(), cx);
5391// editor
5392// .confirm_completion(&ConfirmCompletion::default(), cx)
5393// .unwrap()
5394// });
5395// cx.assert_editor_state(indoc! {"
5396// one.second_completionˇ
5397// two
5398// three
5399// "});
5400
5401// handle_resolve_completion_request(
5402// &mut cx,
5403// Some(vec![
5404// (
5405// //This overlaps with the primary completion edit which is
5406// //misbehavior from the LSP spec, test that we filter it out
5407// indoc! {"
5408// one.second_ˇcompletion
5409// two
5410// threeˇ
5411// "},
5412// "overlapping additional edit",
5413// ),
5414// (
5415// indoc! {"
5416// one.second_completion
5417// two
5418// threeˇ
5419// "},
5420// "\nadditional edit",
5421// ),
5422// ]),
5423// )
5424// .await;
5425// apply_additional_edits.await.unwrap();
5426// cx.assert_editor_state(indoc! {"
5427// one.second_completionˇ
5428// two
5429// three
5430// additional edit
5431// "});
5432
5433// cx.set_state(indoc! {"
5434// one.second_completion
5435// twoˇ
5436// threeˇ
5437// additional edit
5438// "});
5439// cx.simulate_keystroke(" ");
5440// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5441// cx.simulate_keystroke("s");
5442// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5443
5444// cx.assert_editor_state(indoc! {"
5445// one.second_completion
5446// two sˇ
5447// three sˇ
5448// additional edit
5449// "});
5450// handle_completion_request(
5451// &mut cx,
5452// indoc! {"
5453// one.second_completion
5454// two s
5455// three <s|>
5456// additional edit
5457// "},
5458// vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5459// )
5460// .await;
5461// cx.condition(|editor, _| editor.context_menu_visible())
5462// .await;
5463
5464// cx.simulate_keystroke("i");
5465
5466// handle_completion_request(
5467// &mut cx,
5468// indoc! {"
5469// one.second_completion
5470// two si
5471// three <si|>
5472// additional edit
5473// "},
5474// vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5475// )
5476// .await;
5477// cx.condition(|editor, _| editor.context_menu_visible())
5478// .await;
5479
5480// let apply_additional_edits = cx.update_editor(|editor, cx| {
5481// editor
5482// .confirm_completion(&ConfirmCompletion::default(), cx)
5483// .unwrap()
5484// });
5485// cx.assert_editor_state(indoc! {"
5486// one.second_completion
5487// two sixth_completionˇ
5488// three sixth_completionˇ
5489// additional edit
5490// "});
5491
5492// handle_resolve_completion_request(&mut cx, None).await;
5493// apply_additional_edits.await.unwrap();
5494
5495// cx.update(|cx| {
5496// cx.update_global::<SettingsStore, _, _>(|settings, cx| {
5497// settings.update_user_settings::<EditorSettings>(cx, |settings| {
5498// settings.show_completions_on_input = Some(false);
5499// });
5500// })
5501// });
5502// cx.set_state("editorˇ");
5503// cx.simulate_keystroke(".");
5504// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5505// cx.simulate_keystroke("c");
5506// cx.simulate_keystroke("l");
5507// cx.simulate_keystroke("o");
5508// cx.assert_editor_state("editor.cloˇ");
5509// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5510// cx.update_editor(|editor, cx| {
5511// editor.show_completions(&ShowCompletions, cx);
5512// });
5513// handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5514// cx.condition(|editor, _| editor.context_menu_visible())
5515// .await;
5516// let apply_additional_edits = cx.update_editor(|editor, cx| {
5517// editor
5518// .confirm_completion(&ConfirmCompletion::default(), cx)
5519// .unwrap()
5520// });
5521// cx.assert_editor_state("editor.closeˇ");
5522// handle_resolve_completion_request(&mut cx, None).await;
5523// apply_additional_edits.await.unwrap();
5524// }
5525
5526// #[gpui::test]
5527// async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5528// init_test(cx, |_| {});
5529// let mut cx = EditorTestContext::new(cx).await;
5530// let language = Arc::new(Language::new(
5531// LanguageConfig {
5532// line_comment: Some("// ".into()),
5533// ..Default::default()
5534// },
5535// Some(tree_sitter_rust::language()),
5536// ));
5537// cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5538
5539// // If multiple selections intersect a line, the line is only toggled once.
5540// cx.set_state(indoc! {"
5541// fn a() {
5542// «//b();
5543// ˇ»// «c();
5544// //ˇ» d();
5545// }
5546// "});
5547
5548// cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5549
5550// cx.assert_editor_state(indoc! {"
5551// fn a() {
5552// «b();
5553// c();
5554// ˇ» d();
5555// }
5556// "});
5557
5558// // The comment prefix is inserted at the same column for every line in a
5559// // selection.
5560// cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5561
5562// cx.assert_editor_state(indoc! {"
5563// fn a() {
5564// // «b();
5565// // c();
5566// ˇ»// d();
5567// }
5568// "});
5569
5570// // If a selection ends at the beginning of a line, that line is not toggled.
5571// cx.set_selections_state(indoc! {"
5572// fn a() {
5573// // b();
5574// «// c();
5575// ˇ» // d();
5576// }
5577// "});
5578
5579// cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5580
5581// cx.assert_editor_state(indoc! {"
5582// fn a() {
5583// // b();
5584// «c();
5585// ˇ» // d();
5586// }
5587// "});
5588
5589// // If a selection span a single line and is empty, the line is toggled.
5590// cx.set_state(indoc! {"
5591// fn a() {
5592// a();
5593// b();
5594// ˇ
5595// }
5596// "});
5597
5598// cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5599
5600// cx.assert_editor_state(indoc! {"
5601// fn a() {
5602// a();
5603// b();
5604// //•ˇ
5605// }
5606// "});
5607
5608// // If a selection span multiple lines, empty lines are not toggled.
5609// cx.set_state(indoc! {"
5610// fn a() {
5611// «a();
5612
5613// c();ˇ»
5614// }
5615// "});
5616
5617// cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5618
5619// cx.assert_editor_state(indoc! {"
5620// fn a() {
5621// // «a();
5622
5623// // c();ˇ»
5624// }
5625// "});
5626// }
5627
5628// #[gpui::test]
5629// async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5630// init_test(cx, |_| {});
5631
5632// let language = Arc::new(Language::new(
5633// LanguageConfig {
5634// line_comment: Some("// ".into()),
5635// ..Default::default()
5636// },
5637// Some(tree_sitter_rust::language()),
5638// ));
5639
5640// let registry = Arc::new(LanguageRegistry::test());
5641// registry.add(language.clone());
5642
5643// let mut cx = EditorTestContext::new(cx).await;
5644// cx.update_buffer(|buffer, cx| {
5645// buffer.set_language_registry(registry);
5646// buffer.set_language(Some(language), cx);
5647// });
5648
5649// let toggle_comments = &ToggleComments {
5650// advance_downwards: true,
5651// };
5652
5653// // Single cursor on one line -> advance
5654// // Cursor moves horizontally 3 characters as well on non-blank line
5655// cx.set_state(indoc!(
5656// "fn a() {
5657// ˇdog();
5658// cat();
5659// }"
5660// ));
5661// cx.update_editor(|editor, cx| {
5662// editor.toggle_comments(toggle_comments, cx);
5663// });
5664// cx.assert_editor_state(indoc!(
5665// "fn a() {
5666// // dog();
5667// catˇ();
5668// }"
5669// ));
5670
5671// // Single selection on one line -> don't advance
5672// cx.set_state(indoc!(
5673// "fn a() {
5674// «dog()ˇ»;
5675// cat();
5676// }"
5677// ));
5678// cx.update_editor(|editor, cx| {
5679// editor.toggle_comments(toggle_comments, cx);
5680// });
5681// cx.assert_editor_state(indoc!(
5682// "fn a() {
5683// // «dog()ˇ»;
5684// cat();
5685// }"
5686// ));
5687
5688// // Multiple cursors on one line -> advance
5689// cx.set_state(indoc!(
5690// "fn a() {
5691// ˇdˇog();
5692// cat();
5693// }"
5694// ));
5695// cx.update_editor(|editor, cx| {
5696// editor.toggle_comments(toggle_comments, cx);
5697// });
5698// cx.assert_editor_state(indoc!(
5699// "fn a() {
5700// // dog();
5701// catˇ(ˇ);
5702// }"
5703// ));
5704
5705// // Multiple cursors on one line, with selection -> don't advance
5706// cx.set_state(indoc!(
5707// "fn a() {
5708// ˇdˇog«()ˇ»;
5709// cat();
5710// }"
5711// ));
5712// cx.update_editor(|editor, cx| {
5713// editor.toggle_comments(toggle_comments, cx);
5714// });
5715// cx.assert_editor_state(indoc!(
5716// "fn a() {
5717// // ˇdˇog«()ˇ»;
5718// cat();
5719// }"
5720// ));
5721
5722// // Single cursor on one line -> advance
5723// // Cursor moves to column 0 on blank line
5724// cx.set_state(indoc!(
5725// "fn a() {
5726// ˇdog();
5727
5728// cat();
5729// }"
5730// ));
5731// cx.update_editor(|editor, cx| {
5732// editor.toggle_comments(toggle_comments, cx);
5733// });
5734// cx.assert_editor_state(indoc!(
5735// "fn a() {
5736// // dog();
5737// ˇ
5738// cat();
5739// }"
5740// ));
5741
5742// // Single cursor on one line -> advance
5743// // Cursor starts and ends at column 0
5744// cx.set_state(indoc!(
5745// "fn a() {
5746// ˇ dog();
5747// cat();
5748// }"
5749// ));
5750// cx.update_editor(|editor, cx| {
5751// editor.toggle_comments(toggle_comments, cx);
5752// });
5753// cx.assert_editor_state(indoc!(
5754// "fn a() {
5755// // dog();
5756// ˇ cat();
5757// }"
5758// ));
5759// }
5760
5761// #[gpui::test]
5762// async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5763// init_test(cx, |_| {});
5764
5765// let mut cx = EditorTestContext::new(cx).await;
5766
5767// let html_language = Arc::new(
5768// Language::new(
5769// LanguageConfig {
5770// name: "HTML".into(),
5771// block_comment: Some(("<!-- ".into(), " -->".into())),
5772// ..Default::default()
5773// },
5774// Some(tree_sitter_html::language()),
5775// )
5776// .with_injection_query(
5777// r#"
5778// (script_element
5779// (raw_text) @content
5780// (#set! "language" "javascript"))
5781// "#,
5782// )
5783// .unwrap(),
5784// );
5785
5786// let javascript_language = Arc::new(Language::new(
5787// LanguageConfig {
5788// name: "JavaScript".into(),
5789// line_comment: Some("// ".into()),
5790// ..Default::default()
5791// },
5792// Some(tree_sitter_typescript::language_tsx()),
5793// ));
5794
5795// let registry = Arc::new(LanguageRegistry::test());
5796// registry.add(html_language.clone());
5797// registry.add(javascript_language.clone());
5798
5799// cx.update_buffer(|buffer, cx| {
5800// buffer.set_language_registry(registry);
5801// buffer.set_language(Some(html_language), cx);
5802// });
5803
5804// // Toggle comments for empty selections
5805// cx.set_state(
5806// &r#"
5807// <p>A</p>ˇ
5808// <p>B</p>ˇ
5809// <p>C</p>ˇ
5810// "#
5811// .unindent(),
5812// );
5813// cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5814// cx.assert_editor_state(
5815// &r#"
5816// <!-- <p>A</p>ˇ -->
5817// <!-- <p>B</p>ˇ -->
5818// <!-- <p>C</p>ˇ -->
5819// "#
5820// .unindent(),
5821// );
5822// cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5823// cx.assert_editor_state(
5824// &r#"
5825// <p>A</p>ˇ
5826// <p>B</p>ˇ
5827// <p>C</p>ˇ
5828// "#
5829// .unindent(),
5830// );
5831
5832// // Toggle comments for mixture of empty and non-empty selections, where
5833// // multiple selections occupy a given line.
5834// cx.set_state(
5835// &r#"
5836// <p>A«</p>
5837// <p>ˇ»B</p>ˇ
5838// <p>C«</p>
5839// <p>ˇ»D</p>ˇ
5840// "#
5841// .unindent(),
5842// );
5843
5844// cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5845// cx.assert_editor_state(
5846// &r#"
5847// <!-- <p>A«</p>
5848// <p>ˇ»B</p>ˇ -->
5849// <!-- <p>C«</p>
5850// <p>ˇ»D</p>ˇ -->
5851// "#
5852// .unindent(),
5853// );
5854// cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5855// cx.assert_editor_state(
5856// &r#"
5857// <p>A«</p>
5858// <p>ˇ»B</p>ˇ
5859// <p>C«</p>
5860// <p>ˇ»D</p>ˇ
5861// "#
5862// .unindent(),
5863// );
5864
5865// // Toggle comments when different languages are active for different
5866// // selections.
5867// cx.set_state(
5868// &r#"
5869// ˇ<script>
5870// ˇvar x = new Y();
5871// ˇ</script>
5872// "#
5873// .unindent(),
5874// );
5875// cx.foreground().run_until_parked();
5876// cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5877// cx.assert_editor_state(
5878// &r#"
5879// <!-- ˇ<script> -->
5880// // ˇvar x = new Y();
5881// <!-- ˇ</script> -->
5882// "#
5883// .unindent(),
5884// );
5885// }
5886
5887// #[gpui::test]
5888// fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5889// init_test(cx, |_| {});
5890
5891// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
5892// let multibuffer = cx.add_model(|cx| {
5893// let mut multibuffer = MultiBuffer::new(0);
5894// multibuffer.push_excerpts(
5895// buffer.clone(),
5896// [
5897// ExcerptRange {
5898// context: Point::new(0, 0)..Point::new(0, 4),
5899// primary: None,
5900// },
5901// ExcerptRange {
5902// context: Point::new(1, 0)..Point::new(1, 4),
5903// primary: None,
5904// },
5905// ],
5906// cx,
5907// );
5908// assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5909// multibuffer
5910// });
5911
5912// let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5913// view.update(cx, |view, cx| {
5914// assert_eq!(view.text(cx), "aaaa\nbbbb");
5915// view.change_selections(None, cx, |s| {
5916// s.select_ranges([
5917// Point::new(0, 0)..Point::new(0, 0),
5918// Point::new(1, 0)..Point::new(1, 0),
5919// ])
5920// });
5921
5922// view.handle_input("X", cx);
5923// assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5924// assert_eq!(
5925// view.selections.ranges(cx),
5926// [
5927// Point::new(0, 1)..Point::new(0, 1),
5928// Point::new(1, 1)..Point::new(1, 1),
5929// ]
5930// );
5931
5932// // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5933// view.change_selections(None, cx, |s| {
5934// s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5935// });
5936// view.backspace(&Default::default(), cx);
5937// assert_eq!(view.text(cx), "Xa\nbbb");
5938// assert_eq!(
5939// view.selections.ranges(cx),
5940// [Point::new(1, 0)..Point::new(1, 0)]
5941// );
5942
5943// view.change_selections(None, cx, |s| {
5944// s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5945// });
5946// view.backspace(&Default::default(), cx);
5947// assert_eq!(view.text(cx), "X\nbb");
5948// assert_eq!(
5949// view.selections.ranges(cx),
5950// [Point::new(0, 1)..Point::new(0, 1)]
5951// );
5952// });
5953// }
5954
5955// #[gpui::test]
5956// fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5957// init_test(cx, |_| {});
5958
5959// let markers = vec![('[', ']').into(), ('(', ')').into()];
5960// let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5961// indoc! {"
5962// [aaaa
5963// (bbbb]
5964// cccc)",
5965// },
5966// markers.clone(),
5967// );
5968// let excerpt_ranges = markers.into_iter().map(|marker| {
5969// let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5970// ExcerptRange {
5971// context,
5972// primary: None,
5973// }
5974// });
5975// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, initial_text));
5976// let multibuffer = cx.add_model(|cx| {
5977// let mut multibuffer = MultiBuffer::new(0);
5978// multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5979// multibuffer
5980// });
5981
5982// let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5983// view.update(cx, |view, cx| {
5984// let (expected_text, selection_ranges) = marked_text_ranges(
5985// indoc! {"
5986// aaaa
5987// bˇbbb
5988// bˇbbˇb
5989// cccc"
5990// },
5991// true,
5992// );
5993// assert_eq!(view.text(cx), expected_text);
5994// view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5995
5996// view.handle_input("X", cx);
5997
5998// let (expected_text, expected_selections) = marked_text_ranges(
5999// indoc! {"
6000// aaaa
6001// bXˇbbXb
6002// bXˇbbXˇb
6003// cccc"
6004// },
6005// false,
6006// );
6007// assert_eq!(view.text(cx), expected_text);
6008// assert_eq!(view.selections.ranges(cx), expected_selections);
6009
6010// view.newline(&Newline, cx);
6011// let (expected_text, expected_selections) = marked_text_ranges(
6012// indoc! {"
6013// aaaa
6014// bX
6015// ˇbbX
6016// b
6017// bX
6018// ˇbbX
6019// ˇb
6020// cccc"
6021// },
6022// false,
6023// );
6024// assert_eq!(view.text(cx), expected_text);
6025// assert_eq!(view.selections.ranges(cx), expected_selections);
6026// });
6027// }
6028
6029// #[gpui::test]
6030// fn test_refresh_selections(cx: &mut TestAppContext) {
6031// init_test(cx, |_| {});
6032
6033// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
6034// let mut excerpt1_id = None;
6035// let multibuffer = cx.add_model(|cx| {
6036// let mut multibuffer = MultiBuffer::new(0);
6037// excerpt1_id = multibuffer
6038// .push_excerpts(
6039// buffer.clone(),
6040// [
6041// ExcerptRange {
6042// context: Point::new(0, 0)..Point::new(1, 4),
6043// primary: None,
6044// },
6045// ExcerptRange {
6046// context: Point::new(1, 0)..Point::new(2, 4),
6047// primary: None,
6048// },
6049// ],
6050// cx,
6051// )
6052// .into_iter()
6053// .next();
6054// assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6055// multibuffer
6056// });
6057
6058// let editor = cx
6059// .add_window(|cx| {
6060// let mut editor = build_editor(multibuffer.clone(), cx);
6061// let snapshot = editor.snapshot(cx);
6062// editor.change_selections(None, cx, |s| {
6063// s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6064// });
6065// editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6066// assert_eq!(
6067// editor.selections.ranges(cx),
6068// [
6069// Point::new(1, 3)..Point::new(1, 3),
6070// Point::new(2, 1)..Point::new(2, 1),
6071// ]
6072// );
6073// editor
6074// })
6075// .root(cx);
6076
6077// // Refreshing selections is a no-op when excerpts haven't changed.
6078// editor.update(cx, |editor, cx| {
6079// editor.change_selections(None, cx, |s| s.refresh());
6080// assert_eq!(
6081// editor.selections.ranges(cx),
6082// [
6083// Point::new(1, 3)..Point::new(1, 3),
6084// Point::new(2, 1)..Point::new(2, 1),
6085// ]
6086// );
6087// });
6088
6089// multibuffer.update(cx, |multibuffer, cx| {
6090// multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6091// });
6092// editor.update(cx, |editor, cx| {
6093// // Removing an excerpt causes the first selection to become degenerate.
6094// assert_eq!(
6095// editor.selections.ranges(cx),
6096// [
6097// Point::new(0, 0)..Point::new(0, 0),
6098// Point::new(0, 1)..Point::new(0, 1)
6099// ]
6100// );
6101
6102// // Refreshing selections will relocate the first selection to the original buffer
6103// // location.
6104// editor.change_selections(None, cx, |s| s.refresh());
6105// assert_eq!(
6106// editor.selections.ranges(cx),
6107// [
6108// Point::new(0, 1)..Point::new(0, 1),
6109// Point::new(0, 3)..Point::new(0, 3)
6110// ]
6111// );
6112// assert!(editor.selections.pending_anchor().is_some());
6113// });
6114// }
6115
6116// #[gpui::test]
6117// fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6118// init_test(cx, |_| {});
6119
6120// let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
6121// let mut excerpt1_id = None;
6122// let multibuffer = cx.add_model(|cx| {
6123// let mut multibuffer = MultiBuffer::new(0);
6124// excerpt1_id = multibuffer
6125// .push_excerpts(
6126// buffer.clone(),
6127// [
6128// ExcerptRange {
6129// context: Point::new(0, 0)..Point::new(1, 4),
6130// primary: None,
6131// },
6132// ExcerptRange {
6133// context: Point::new(1, 0)..Point::new(2, 4),
6134// primary: None,
6135// },
6136// ],
6137// cx,
6138// )
6139// .into_iter()
6140// .next();
6141// assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6142// multibuffer
6143// });
6144
6145// let editor = cx
6146// .add_window(|cx| {
6147// let mut editor = build_editor(multibuffer.clone(), cx);
6148// let snapshot = editor.snapshot(cx);
6149// editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6150// assert_eq!(
6151// editor.selections.ranges(cx),
6152// [Point::new(1, 3)..Point::new(1, 3)]
6153// );
6154// editor
6155// })
6156// .root(cx);
6157
6158// multibuffer.update(cx, |multibuffer, cx| {
6159// multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6160// });
6161// editor.update(cx, |editor, cx| {
6162// assert_eq!(
6163// editor.selections.ranges(cx),
6164// [Point::new(0, 0)..Point::new(0, 0)]
6165// );
6166
6167// // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6168// editor.change_selections(None, cx, |s| s.refresh());
6169// assert_eq!(
6170// editor.selections.ranges(cx),
6171// [Point::new(0, 3)..Point::new(0, 3)]
6172// );
6173// assert!(editor.selections.pending_anchor().is_some());
6174// });
6175// }
6176
6177// #[gpui::test]
6178// async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6179// init_test(cx, |_| {});
6180
6181// let language = Arc::new(
6182// Language::new(
6183// LanguageConfig {
6184// brackets: BracketPairConfig {
6185// pairs: vec![
6186// BracketPair {
6187// start: "{".to_string(),
6188// end: "}".to_string(),
6189// close: true,
6190// newline: true,
6191// },
6192// BracketPair {
6193// start: "/* ".to_string(),
6194// end: " */".to_string(),
6195// close: true,
6196// newline: true,
6197// },
6198// ],
6199// ..Default::default()
6200// },
6201// ..Default::default()
6202// },
6203// Some(tree_sitter_rust::language()),
6204// )
6205// .with_indents_query("")
6206// .unwrap(),
6207// );
6208
6209// let text = concat!(
6210// "{ }\n", //
6211// " x\n", //
6212// " /* */\n", //
6213// "x\n", //
6214// "{{} }\n", //
6215// );
6216
6217// let buffer =
6218// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
6219// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6220// let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
6221// view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6222// .await;
6223
6224// view.update(cx, |view, cx| {
6225// view.change_selections(None, cx, |s| {
6226// s.select_display_ranges([
6227// DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6228// DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6229// DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6230// ])
6231// });
6232// view.newline(&Newline, cx);
6233
6234// assert_eq!(
6235// view.buffer().read(cx).read(cx).text(),
6236// concat!(
6237// "{ \n", // Suppress rustfmt
6238// "\n", //
6239// "}\n", //
6240// " x\n", //
6241// " /* \n", //
6242// " \n", //
6243// " */\n", //
6244// "x\n", //
6245// "{{} \n", //
6246// "}\n", //
6247// )
6248// );
6249// });
6250// }
6251
6252// #[gpui::test]
6253// fn test_highlighted_ranges(cx: &mut TestAppContext) {
6254// init_test(cx, |_| {});
6255
6256// let editor = cx
6257// .add_window(|cx| {
6258// let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6259// build_editor(buffer.clone(), cx)
6260// })
6261// .root(cx);
6262
6263// editor.update(cx, |editor, cx| {
6264// struct Type1;
6265// struct Type2;
6266
6267// let buffer = editor.buffer.read(cx).snapshot(cx);
6268
6269// let anchor_range =
6270// |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6271
6272// editor.highlight_background::<Type1>(
6273// vec![
6274// anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6275// anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6276// anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6277// anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6278// ],
6279// |_| Hsla::red(),
6280// cx,
6281// );
6282// editor.highlight_background::<Type2>(
6283// vec![
6284// anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6285// anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6286// anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6287// anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6288// ],
6289// |_| Hsla::green(),
6290// cx,
6291// );
6292
6293// let snapshot = editor.snapshot(cx);
6294// let mut highlighted_ranges = editor.background_highlights_in_range(
6295// anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6296// &snapshot,
6297// theme::current(cx).as_ref(),
6298// );
6299// // Enforce a consistent ordering based on color without relying on the ordering of the
6300// // highlight's `TypeId` which is non-deterministic.
6301// highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6302// assert_eq!(
6303// highlighted_ranges,
6304// &[
6305// (
6306// DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6307// Hsla::green(),
6308// ),
6309// (
6310// DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6311// Hsla::green(),
6312// ),
6313// (
6314// DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6315// Hsla::red(),
6316// ),
6317// (
6318// DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6319// Hsla::red(),
6320// ),
6321// ]
6322// );
6323// assert_eq!(
6324// editor.background_highlights_in_range(
6325// anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6326// &snapshot,
6327// theme::current(cx).as_ref(),
6328// ),
6329// &[(
6330// DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6331// Hsla::red(),
6332// )]
6333// );
6334// });
6335// }
6336
6337// #[gpui::test]
6338// async fn test_following(cx: &mut gpui::TestAppContext) {
6339// init_test(cx, |_| {});
6340
6341// let fs = FakeFs::new(cx.background());
6342// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6343
6344// let buffer = project.update(cx, |project, cx| {
6345// let buffer = project
6346// .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6347// .unwrap();
6348// cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
6349// });
6350// let leader = cx
6351// .add_window(|cx| build_editor(buffer.clone(), cx))
6352// .root(cx);
6353// let follower = cx
6354// .update(|cx| {
6355// cx.add_window(
6356// WindowOptions {
6357// bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
6358// ..Default::default()
6359// },
6360// |cx| build_editor(buffer.clone(), cx),
6361// )
6362// })
6363// .root(cx);
6364
6365// let is_still_following = Rc::new(RefCell::new(true));
6366// let follower_edit_event_count = Rc::new(RefCell::new(0));
6367// let pending_update = Rc::new(RefCell::new(None));
6368// follower.update(cx, {
6369// let update = pending_update.clone();
6370// let is_still_following = is_still_following.clone();
6371// let follower_edit_event_count = follower_edit_event_count.clone();
6372// |_, cx| {
6373// cx.subscribe(&leader, move |_, leader, event, cx| {
6374// leader
6375// .read(cx)
6376// .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6377// })
6378// .detach();
6379
6380// cx.subscribe(&follower, move |_, _, event, cx| {
6381// if Editor::should_unfollow_on_event(event, cx) {
6382// *is_still_following.borrow_mut() = false;
6383// }
6384// if let Event::BufferEdited = event {
6385// *follower_edit_event_count.borrow_mut() += 1;
6386// }
6387// })
6388// .detach();
6389// }
6390// });
6391
6392// // Update the selections only
6393// leader.update(cx, |leader, cx| {
6394// leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6395// });
6396// follower
6397// .update(cx, |follower, cx| {
6398// follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6399// })
6400// .await
6401// .unwrap();
6402// follower.read_with(cx, |follower, cx| {
6403// assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6404// });
6405// assert_eq!(*is_still_following.borrow(), true);
6406// assert_eq!(*follower_edit_event_count.borrow(), 0);
6407
6408// // Update the scroll position only
6409// leader.update(cx, |leader, cx| {
6410// leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6411// });
6412// follower
6413// .update(cx, |follower, cx| {
6414// follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6415// })
6416// .await
6417// .unwrap();
6418// assert_eq!(
6419// follower.update(cx, |follower, cx| follower.scroll_position(cx)),
6420// vec2f(1.5, 3.5)
6421// );
6422// assert_eq!(*is_still_following.borrow(), true);
6423// assert_eq!(*follower_edit_event_count.borrow(), 0);
6424
6425// // Update the selections and scroll position. The follower's scroll position is updated
6426// // via autoscroll, not via the leader's exact scroll position.
6427// leader.update(cx, |leader, cx| {
6428// leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6429// leader.request_autoscroll(Autoscroll::newest(), cx);
6430// leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6431// });
6432// follower
6433// .update(cx, |follower, cx| {
6434// follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6435// })
6436// .await
6437// .unwrap();
6438// follower.update(cx, |follower, cx| {
6439// assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
6440// assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6441// });
6442// assert_eq!(*is_still_following.borrow(), true);
6443
6444// // Creating a pending selection that precedes another selection
6445// leader.update(cx, |leader, cx| {
6446// leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6447// leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6448// });
6449// follower
6450// .update(cx, |follower, cx| {
6451// follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6452// })
6453// .await
6454// .unwrap();
6455// follower.read_with(cx, |follower, cx| {
6456// assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6457// });
6458// assert_eq!(*is_still_following.borrow(), true);
6459
6460// // Extend the pending selection so that it surrounds another selection
6461// leader.update(cx, |leader, cx| {
6462// leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6463// });
6464// follower
6465// .update(cx, |follower, cx| {
6466// follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6467// })
6468// .await
6469// .unwrap();
6470// follower.read_with(cx, |follower, cx| {
6471// assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6472// });
6473
6474// // Scrolling locally breaks the follow
6475// follower.update(cx, |follower, cx| {
6476// let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6477// follower.set_scroll_anchor(
6478// ScrollAnchor {
6479// anchor: top_anchor,
6480// offset: vec2f(0.0, 0.5),
6481// },
6482// cx,
6483// );
6484// });
6485// assert_eq!(*is_still_following.borrow(), false);
6486// }
6487
6488// #[gpui::test]
6489// async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6490// init_test(cx, |_| {});
6491
6492// let fs = FakeFs::new(cx.background());
6493// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6494// let workspace = cx
6495// .add_window(|cx| Workspace::test_new(project.clone(), cx))
6496// .root(cx);
6497// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6498
6499// let leader = pane.update(cx, |_, cx| {
6500// let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
6501// cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
6502// });
6503
6504// // Start following the editor when it has no excerpts.
6505// let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6506// let follower_1 = cx
6507// .update(|cx| {
6508// Editor::from_state_proto(
6509// pane.clone(),
6510// workspace.clone(),
6511// ViewId {
6512// creator: Default::default(),
6513// id: 0,
6514// },
6515// &mut state_message,
6516// cx,
6517// )
6518// })
6519// .unwrap()
6520// .await
6521// .unwrap();
6522
6523// let update_message = Rc::new(RefCell::new(None));
6524// follower_1.update(cx, {
6525// let update = update_message.clone();
6526// |_, cx| {
6527// cx.subscribe(&leader, move |_, leader, event, cx| {
6528// leader
6529// .read(cx)
6530// .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6531// })
6532// .detach();
6533// }
6534// });
6535
6536// let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6537// (
6538// project
6539// .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6540// .unwrap(),
6541// project
6542// .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6543// .unwrap(),
6544// )
6545// });
6546
6547// // Insert some excerpts.
6548// leader.update(cx, |leader, cx| {
6549// leader.buffer.update(cx, |multibuffer, cx| {
6550// let excerpt_ids = multibuffer.push_excerpts(
6551// buffer_1.clone(),
6552// [
6553// ExcerptRange {
6554// context: 1..6,
6555// primary: None,
6556// },
6557// ExcerptRange {
6558// context: 12..15,
6559// primary: None,
6560// },
6561// ExcerptRange {
6562// context: 0..3,
6563// primary: None,
6564// },
6565// ],
6566// cx,
6567// );
6568// multibuffer.insert_excerpts_after(
6569// excerpt_ids[0],
6570// buffer_2.clone(),
6571// [
6572// ExcerptRange {
6573// context: 8..12,
6574// primary: None,
6575// },
6576// ExcerptRange {
6577// context: 0..6,
6578// primary: None,
6579// },
6580// ],
6581// cx,
6582// );
6583// });
6584// });
6585
6586// // Apply the update of adding the excerpts.
6587// follower_1
6588// .update(cx, |follower, cx| {
6589// follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6590// })
6591// .await
6592// .unwrap();
6593// assert_eq!(
6594// follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6595// leader.read_with(cx, |editor, cx| editor.text(cx))
6596// );
6597// update_message.borrow_mut().take();
6598
6599// // Start following separately after it already has excerpts.
6600// let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6601// let follower_2 = cx
6602// .update(|cx| {
6603// Editor::from_state_proto(
6604// pane.clone(),
6605// workspace.clone(),
6606// ViewId {
6607// creator: Default::default(),
6608// id: 0,
6609// },
6610// &mut state_message,
6611// cx,
6612// )
6613// })
6614// .unwrap()
6615// .await
6616// .unwrap();
6617// assert_eq!(
6618// follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6619// leader.read_with(cx, |editor, cx| editor.text(cx))
6620// );
6621
6622// // Remove some excerpts.
6623// leader.update(cx, |leader, cx| {
6624// leader.buffer.update(cx, |multibuffer, cx| {
6625// let excerpt_ids = multibuffer.excerpt_ids();
6626// multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6627// multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6628// });
6629// });
6630
6631// // Apply the update of removing the excerpts.
6632// follower_1
6633// .update(cx, |follower, cx| {
6634// follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6635// })
6636// .await
6637// .unwrap();
6638// follower_2
6639// .update(cx, |follower, cx| {
6640// follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6641// })
6642// .await
6643// .unwrap();
6644// update_message.borrow_mut().take();
6645// assert_eq!(
6646// follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6647// leader.read_with(cx, |editor, cx| editor.text(cx))
6648// );
6649// }
6650
6651// #[test]
6652// fn test_combine_syntax_and_fuzzy_match_highlights() {
6653// let string = "abcdefghijklmnop";
6654// let syntax_ranges = [
6655// (
6656// 0..3,
6657// HighlightStyle {
6658// color: Some(Hsla::red()),
6659// ..Default::default()
6660// },
6661// ),
6662// (
6663// 4..8,
6664// HighlightStyle {
6665// color: Some(Hsla::green()),
6666// ..Default::default()
6667// },
6668// ),
6669// ];
6670// let match_indices = [4, 6, 7, 8];
6671// assert_eq!(
6672// combine_syntax_and_fuzzy_match_highlights(
6673// string,
6674// Default::default(),
6675// syntax_ranges.into_iter(),
6676// &match_indices,
6677// ),
6678// &[
6679// (
6680// 0..3,
6681// HighlightStyle {
6682// color: Some(Hsla::red()),
6683// ..Default::default()
6684// },
6685// ),
6686// (
6687// 4..5,
6688// HighlightStyle {
6689// color: Some(Hsla::green()),
6690// weight: Some(fonts::Weight::BOLD),
6691// ..Default::default()
6692// },
6693// ),
6694// (
6695// 5..6,
6696// HighlightStyle {
6697// color: Some(Hsla::green()),
6698// ..Default::default()
6699// },
6700// ),
6701// (
6702// 6..8,
6703// HighlightStyle {
6704// color: Some(Hsla::green()),
6705// weight: Some(fonts::Weight::BOLD),
6706// ..Default::default()
6707// },
6708// ),
6709// (
6710// 8..9,
6711// HighlightStyle {
6712// weight: Some(fonts::Weight::BOLD),
6713// ..Default::default()
6714// },
6715// ),
6716// ]
6717// );
6718// }
6719
6720// #[gpui::test]
6721// async fn go_to_prev_overlapping_diagnostic(
6722// deterministic: Arc<Deterministic>,
6723// cx: &mut gpui::TestAppContext,
6724// ) {
6725// init_test(cx, |_| {});
6726
6727// let mut cx = EditorTestContext::new(cx).await;
6728// let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
6729
6730// cx.set_state(indoc! {"
6731// ˇfn func(abc def: i32) -> u32 {
6732// }
6733// "});
6734
6735// cx.update(|cx| {
6736// project.update(cx, |project, cx| {
6737// project
6738// .update_diagnostics(
6739// LanguageServerId(0),
6740// lsp::PublishDiagnosticsParams {
6741// uri: lsp::Url::from_file_path("/root/file").unwrap(),
6742// version: None,
6743// diagnostics: vec![
6744// lsp::Diagnostic {
6745// range: lsp::Range::new(
6746// lsp::Position::new(0, 11),
6747// lsp::Position::new(0, 12),
6748// ),
6749// severity: Some(lsp::DiagnosticSeverity::ERROR),
6750// ..Default::default()
6751// },
6752// lsp::Diagnostic {
6753// range: lsp::Range::new(
6754// lsp::Position::new(0, 12),
6755// lsp::Position::new(0, 15),
6756// ),
6757// severity: Some(lsp::DiagnosticSeverity::ERROR),
6758// ..Default::default()
6759// },
6760// lsp::Diagnostic {
6761// range: lsp::Range::new(
6762// lsp::Position::new(0, 25),
6763// lsp::Position::new(0, 28),
6764// ),
6765// severity: Some(lsp::DiagnosticSeverity::ERROR),
6766// ..Default::default()
6767// },
6768// ],
6769// },
6770// &[],
6771// cx,
6772// )
6773// .unwrap()
6774// });
6775// });
6776
6777// deterministic.run_until_parked();
6778
6779// cx.update_editor(|editor, cx| {
6780// editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6781// });
6782
6783// cx.assert_editor_state(indoc! {"
6784// fn func(abc def: i32) -> ˇu32 {
6785// }
6786// "});
6787
6788// cx.update_editor(|editor, cx| {
6789// editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6790// });
6791
6792// cx.assert_editor_state(indoc! {"
6793// fn func(abc ˇdef: i32) -> u32 {
6794// }
6795// "});
6796
6797// cx.update_editor(|editor, cx| {
6798// editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6799// });
6800
6801// cx.assert_editor_state(indoc! {"
6802// fn func(abcˇ def: i32) -> u32 {
6803// }
6804// "});
6805
6806// cx.update_editor(|editor, cx| {
6807// editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
6808// });
6809
6810// cx.assert_editor_state(indoc! {"
6811// fn func(abc def: i32) -> ˇu32 {
6812// }
6813// "});
6814// }
6815
6816// #[gpui::test]
6817// async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6818// init_test(cx, |_| {});
6819
6820// let mut cx = EditorTestContext::new(cx).await;
6821
6822// let diff_base = r#"
6823// use some::mod;
6824
6825// const A: u32 = 42;
6826
6827// fn main() {
6828// println!("hello");
6829
6830// println!("world");
6831// }
6832// "#
6833// .unindent();
6834
6835// // Edits are modified, removed, modified, added
6836// cx.set_state(
6837// &r#"
6838// use some::modified;
6839
6840// ˇ
6841// fn main() {
6842// println!("hello there");
6843
6844// println!("around the");
6845// println!("world");
6846// }
6847// "#
6848// .unindent(),
6849// );
6850
6851// cx.set_diff_base(Some(&diff_base));
6852// deterministic.run_until_parked();
6853
6854// cx.update_editor(|editor, cx| {
6855// //Wrap around the bottom of the buffer
6856// for _ in 0..3 {
6857// editor.go_to_hunk(&GoToHunk, cx);
6858// }
6859// });
6860
6861// cx.assert_editor_state(
6862// &r#"
6863// ˇuse some::modified;
6864
6865// fn main() {
6866// println!("hello there");
6867
6868// println!("around the");
6869// println!("world");
6870// }
6871// "#
6872// .unindent(),
6873// );
6874
6875// cx.update_editor(|editor, cx| {
6876// //Wrap around the top of the buffer
6877// for _ in 0..2 {
6878// editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6879// }
6880// });
6881
6882// cx.assert_editor_state(
6883// &r#"
6884// use some::modified;
6885
6886// fn main() {
6887// ˇ println!("hello there");
6888
6889// println!("around the");
6890// println!("world");
6891// }
6892// "#
6893// .unindent(),
6894// );
6895
6896// cx.update_editor(|editor, cx| {
6897// editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6898// });
6899
6900// cx.assert_editor_state(
6901// &r#"
6902// use some::modified;
6903
6904// ˇ
6905// fn main() {
6906// println!("hello there");
6907
6908// println!("around the");
6909// println!("world");
6910// }
6911// "#
6912// .unindent(),
6913// );
6914
6915// cx.update_editor(|editor, cx| {
6916// for _ in 0..3 {
6917// editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6918// }
6919// });
6920
6921// cx.assert_editor_state(
6922// &r#"
6923// use some::modified;
6924
6925// fn main() {
6926// ˇ println!("hello there");
6927
6928// println!("around the");
6929// println!("world");
6930// }
6931// "#
6932// .unindent(),
6933// );
6934
6935// cx.update_editor(|editor, cx| {
6936// editor.fold(&Fold, cx);
6937
6938// //Make sure that the fold only gets one hunk
6939// for _ in 0..4 {
6940// editor.go_to_hunk(&GoToHunk, cx);
6941// }
6942// });
6943
6944// cx.assert_editor_state(
6945// &r#"
6946// ˇuse some::modified;
6947
6948// fn main() {
6949// println!("hello there");
6950
6951// println!("around the");
6952// println!("world");
6953// }
6954// "#
6955// .unindent(),
6956// );
6957// }
6958
6959// #[test]
6960// fn test_split_words() {
6961// fn split<'a>(text: &'a str) -> Vec<&'a str> {
6962// split_words(text).collect()
6963// }
6964
6965// assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6966// assert_eq!(split("hello_world"), &["hello_", "world"]);
6967// assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6968// assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6969// assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6970// assert_eq!(split("helloworld"), &["helloworld"]);
6971// }
6972
6973// #[gpui::test]
6974// async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6975// init_test(cx, |_| {});
6976
6977// let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6978// let mut assert = |before, after| {
6979// let _state_context = cx.set_state(before);
6980// cx.update_editor(|editor, cx| {
6981// editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6982// });
6983// cx.assert_editor_state(after);
6984// };
6985
6986// // Outside bracket jumps to outside of matching bracket
6987// assert("console.logˇ(var);", "console.log(var)ˇ;");
6988// assert("console.log(var)ˇ;", "console.logˇ(var);");
6989
6990// // Inside bracket jumps to inside of matching bracket
6991// assert("console.log(ˇvar);", "console.log(varˇ);");
6992// assert("console.log(varˇ);", "console.log(ˇvar);");
6993
6994// // When outside a bracket and inside, favor jumping to the inside bracket
6995// assert(
6996// "console.log('foo', [1, 2, 3]ˇ);",
6997// "console.log(ˇ'foo', [1, 2, 3]);",
6998// );
6999// assert(
7000// "console.log(ˇ'foo', [1, 2, 3]);",
7001// "console.log('foo', [1, 2, 3]ˇ);",
7002// );
7003
7004// // Bias forward if two options are equally likely
7005// assert(
7006// "let result = curried_fun()ˇ();",
7007// "let result = curried_fun()()ˇ;",
7008// );
7009
7010// // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7011// assert(
7012// indoc! {"
7013// function test() {
7014// console.log('test')ˇ
7015// }"},
7016// indoc! {"
7017// function test() {
7018// console.logˇ('test')
7019// }"},
7020// );
7021// }
7022
7023// #[gpui::test(iterations = 10)]
7024// async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
7025// init_test(cx, |_| {});
7026
7027// let (copilot, copilot_lsp) = Copilot::fake(cx);
7028// cx.update(|cx| cx.set_global(copilot));
7029// let mut cx = EditorLspTestContext::new_rust(
7030// lsp::ServerCapabilities {
7031// completion_provider: Some(lsp::CompletionOptions {
7032// trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7033// ..Default::default()
7034// }),
7035// ..Default::default()
7036// },
7037// cx,
7038// )
7039// .await;
7040
7041// // When inserting, ensure autocompletion is favored over Copilot suggestions.
7042// cx.set_state(indoc! {"
7043// oneˇ
7044// two
7045// three
7046// "});
7047// cx.simulate_keystroke(".");
7048// let _ = handle_completion_request(
7049// &mut cx,
7050// indoc! {"
7051// one.|<>
7052// two
7053// three
7054// "},
7055// vec!["completion_a", "completion_b"],
7056// );
7057// handle_copilot_completion_request(
7058// &copilot_lsp,
7059// vec![copilot::request::Completion {
7060// text: "one.copilot1".into(),
7061// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7062// ..Default::default()
7063// }],
7064// vec![],
7065// );
7066// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7067// cx.update_editor(|editor, cx| {
7068// assert!(editor.context_menu_visible());
7069// assert!(!editor.has_active_copilot_suggestion(cx));
7070
7071// // Confirming a completion inserts it and hides the context menu, without showing
7072// // the copilot suggestion afterwards.
7073// editor
7074// .confirm_completion(&Default::default(), cx)
7075// .unwrap()
7076// .detach();
7077// assert!(!editor.context_menu_visible());
7078// assert!(!editor.has_active_copilot_suggestion(cx));
7079// assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7080// assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7081// });
7082
7083// // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7084// cx.set_state(indoc! {"
7085// oneˇ
7086// two
7087// three
7088// "});
7089// cx.simulate_keystroke(".");
7090// let _ = handle_completion_request(
7091// &mut cx,
7092// indoc! {"
7093// one.|<>
7094// two
7095// three
7096// "},
7097// vec![],
7098// );
7099// handle_copilot_completion_request(
7100// &copilot_lsp,
7101// vec![copilot::request::Completion {
7102// text: "one.copilot1".into(),
7103// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7104// ..Default::default()
7105// }],
7106// vec![],
7107// );
7108// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7109// cx.update_editor(|editor, cx| {
7110// assert!(!editor.context_menu_visible());
7111// assert!(editor.has_active_copilot_suggestion(cx));
7112// assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7113// assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7114// });
7115
7116// // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7117// cx.set_state(indoc! {"
7118// oneˇ
7119// two
7120// three
7121// "});
7122// cx.simulate_keystroke(".");
7123// let _ = handle_completion_request(
7124// &mut cx,
7125// indoc! {"
7126// one.|<>
7127// two
7128// three
7129// "},
7130// vec!["completion_a", "completion_b"],
7131// );
7132// handle_copilot_completion_request(
7133// &copilot_lsp,
7134// vec![copilot::request::Completion {
7135// text: "one.copilot1".into(),
7136// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7137// ..Default::default()
7138// }],
7139// vec![],
7140// );
7141// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7142// cx.update_editor(|editor, cx| {
7143// assert!(editor.context_menu_visible());
7144// assert!(!editor.has_active_copilot_suggestion(cx));
7145
7146// // When hiding the context menu, the Copilot suggestion becomes visible.
7147// editor.hide_context_menu(cx);
7148// assert!(!editor.context_menu_visible());
7149// assert!(editor.has_active_copilot_suggestion(cx));
7150// assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7151// assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7152// });
7153
7154// // Ensure existing completion is interpolated when inserting again.
7155// cx.simulate_keystroke("c");
7156// deterministic.run_until_parked();
7157// cx.update_editor(|editor, cx| {
7158// assert!(!editor.context_menu_visible());
7159// assert!(editor.has_active_copilot_suggestion(cx));
7160// assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7161// assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7162// });
7163
7164// // After debouncing, new Copilot completions should be requested.
7165// handle_copilot_completion_request(
7166// &copilot_lsp,
7167// vec![copilot::request::Completion {
7168// text: "one.copilot2".into(),
7169// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7170// ..Default::default()
7171// }],
7172// vec![],
7173// );
7174// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7175// cx.update_editor(|editor, cx| {
7176// assert!(!editor.context_menu_visible());
7177// assert!(editor.has_active_copilot_suggestion(cx));
7178// assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7179// assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7180
7181// // Canceling should remove the active Copilot suggestion.
7182// editor.cancel(&Default::default(), cx);
7183// assert!(!editor.has_active_copilot_suggestion(cx));
7184// assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7185// assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7186
7187// // After canceling, tabbing shouldn't insert the previously shown suggestion.
7188// editor.tab(&Default::default(), cx);
7189// assert!(!editor.has_active_copilot_suggestion(cx));
7190// assert_eq!(editor.display_text(cx), "one.c \ntwo\nthree\n");
7191// assert_eq!(editor.text(cx), "one.c \ntwo\nthree\n");
7192
7193// // When undoing the previously active suggestion is shown again.
7194// editor.undo(&Default::default(), cx);
7195// assert!(editor.has_active_copilot_suggestion(cx));
7196// assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7197// assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7198// });
7199
7200// // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7201// cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7202// cx.update_editor(|editor, cx| {
7203// assert!(editor.has_active_copilot_suggestion(cx));
7204// assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7205// assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7206
7207// // Tabbing when there is an active suggestion inserts it.
7208// editor.tab(&Default::default(), cx);
7209// assert!(!editor.has_active_copilot_suggestion(cx));
7210// assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7211// assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7212
7213// // When undoing the previously active suggestion is shown again.
7214// editor.undo(&Default::default(), cx);
7215// assert!(editor.has_active_copilot_suggestion(cx));
7216// assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7217// assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7218
7219// // Hide suggestion.
7220// editor.cancel(&Default::default(), cx);
7221// assert!(!editor.has_active_copilot_suggestion(cx));
7222// assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7223// assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7224// });
7225
7226// // If an edit occurs outside of this editor but no suggestion is being shown,
7227// // we won't make it visible.
7228// cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7229// cx.update_editor(|editor, cx| {
7230// assert!(!editor.has_active_copilot_suggestion(cx));
7231// assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7232// assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7233// });
7234
7235// // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7236// cx.update_editor(|editor, cx| {
7237// editor.set_text("fn foo() {\n \n}", cx);
7238// editor.change_selections(None, cx, |s| {
7239// s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7240// });
7241// });
7242// handle_copilot_completion_request(
7243// &copilot_lsp,
7244// vec![copilot::request::Completion {
7245// text: " let x = 4;".into(),
7246// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7247// ..Default::default()
7248// }],
7249// vec![],
7250// );
7251
7252// cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7253// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7254// cx.update_editor(|editor, cx| {
7255// assert!(editor.has_active_copilot_suggestion(cx));
7256// assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
7257// assert_eq!(editor.text(cx), "fn foo() {\n \n}");
7258
7259// // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7260// editor.tab(&Default::default(), cx);
7261// assert!(editor.has_active_copilot_suggestion(cx));
7262// assert_eq!(editor.text(cx), "fn foo() {\n \n}");
7263// assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
7264
7265// // Tabbing again accepts the suggestion.
7266// editor.tab(&Default::default(), cx);
7267// assert!(!editor.has_active_copilot_suggestion(cx));
7268// assert_eq!(editor.text(cx), "fn foo() {\n let x = 4;\n}");
7269// assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
7270// });
7271// }
7272
7273// #[gpui::test]
7274// async fn test_copilot_completion_invalidation(
7275// deterministic: Arc<Deterministic>,
7276// cx: &mut gpui::TestAppContext,
7277// ) {
7278// init_test(cx, |_| {});
7279
7280// let (copilot, copilot_lsp) = Copilot::fake(cx);
7281// cx.update(|cx| cx.set_global(copilot));
7282// let mut cx = EditorLspTestContext::new_rust(
7283// lsp::ServerCapabilities {
7284// completion_provider: Some(lsp::CompletionOptions {
7285// trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7286// ..Default::default()
7287// }),
7288// ..Default::default()
7289// },
7290// cx,
7291// )
7292// .await;
7293
7294// cx.set_state(indoc! {"
7295// one
7296// twˇ
7297// three
7298// "});
7299
7300// handle_copilot_completion_request(
7301// &copilot_lsp,
7302// vec![copilot::request::Completion {
7303// text: "two.foo()".into(),
7304// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7305// ..Default::default()
7306// }],
7307// vec![],
7308// );
7309// cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7310// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7311// cx.update_editor(|editor, cx| {
7312// assert!(editor.has_active_copilot_suggestion(cx));
7313// assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7314// assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7315
7316// editor.backspace(&Default::default(), cx);
7317// assert!(editor.has_active_copilot_suggestion(cx));
7318// assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7319// assert_eq!(editor.text(cx), "one\nt\nthree\n");
7320
7321// editor.backspace(&Default::default(), cx);
7322// assert!(editor.has_active_copilot_suggestion(cx));
7323// assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7324// assert_eq!(editor.text(cx), "one\n\nthree\n");
7325
7326// // Deleting across the original suggestion range invalidates it.
7327// editor.backspace(&Default::default(), cx);
7328// assert!(!editor.has_active_copilot_suggestion(cx));
7329// assert_eq!(editor.display_text(cx), "one\nthree\n");
7330// assert_eq!(editor.text(cx), "one\nthree\n");
7331
7332// // Undoing the deletion restores the suggestion.
7333// editor.undo(&Default::default(), cx);
7334// assert!(editor.has_active_copilot_suggestion(cx));
7335// assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7336// assert_eq!(editor.text(cx), "one\n\nthree\n");
7337// });
7338// }
7339
7340// #[gpui::test]
7341// async fn test_copilot_multibuffer(
7342// deterministic: Arc<Deterministic>,
7343// cx: &mut gpui::TestAppContext,
7344// ) {
7345// init_test(cx, |_| {});
7346
7347// let (copilot, copilot_lsp) = Copilot::fake(cx);
7348// cx.update(|cx| cx.set_global(copilot));
7349
7350// let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n"));
7351// let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "c = 3\nd = 4\n"));
7352// let multibuffer = cx.add_model(|cx| {
7353// let mut multibuffer = MultiBuffer::new(0);
7354// multibuffer.push_excerpts(
7355// buffer_1.clone(),
7356// [ExcerptRange {
7357// context: Point::new(0, 0)..Point::new(2, 0),
7358// primary: None,
7359// }],
7360// cx,
7361// );
7362// multibuffer.push_excerpts(
7363// buffer_2.clone(),
7364// [ExcerptRange {
7365// context: Point::new(0, 0)..Point::new(2, 0),
7366// primary: None,
7367// }],
7368// cx,
7369// );
7370// multibuffer
7371// });
7372// let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7373
7374// handle_copilot_completion_request(
7375// &copilot_lsp,
7376// vec![copilot::request::Completion {
7377// text: "b = 2 + a".into(),
7378// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7379// ..Default::default()
7380// }],
7381// vec![],
7382// );
7383// editor.update(cx, |editor, cx| {
7384// // Ensure copilot suggestions are shown for the first excerpt.
7385// editor.change_selections(None, cx, |s| {
7386// s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7387// });
7388// editor.next_copilot_suggestion(&Default::default(), cx);
7389// });
7390// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7391// editor.update(cx, |editor, cx| {
7392// assert!(editor.has_active_copilot_suggestion(cx));
7393// assert_eq!(
7394// editor.display_text(cx),
7395// "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7396// );
7397// assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7398// });
7399
7400// handle_copilot_completion_request(
7401// &copilot_lsp,
7402// vec![copilot::request::Completion {
7403// text: "d = 4 + c".into(),
7404// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7405// ..Default::default()
7406// }],
7407// vec![],
7408// );
7409// editor.update(cx, |editor, cx| {
7410// // Move to another excerpt, ensuring the suggestion gets cleared.
7411// editor.change_selections(None, cx, |s| {
7412// s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7413// });
7414// assert!(!editor.has_active_copilot_suggestion(cx));
7415// assert_eq!(
7416// editor.display_text(cx),
7417// "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7418// );
7419// assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7420
7421// // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7422// editor.handle_input(" ", cx);
7423// assert!(!editor.has_active_copilot_suggestion(cx));
7424// assert_eq!(
7425// editor.display_text(cx),
7426// "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7427// );
7428// assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7429// });
7430
7431// // Ensure the new suggestion is displayed when the debounce timeout expires.
7432// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7433// editor.update(cx, |editor, cx| {
7434// assert!(editor.has_active_copilot_suggestion(cx));
7435// assert_eq!(
7436// editor.display_text(cx),
7437// "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7438// );
7439// assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7440// });
7441// }
7442
7443// #[gpui::test]
7444// async fn test_copilot_disabled_globs(
7445// deterministic: Arc<Deterministic>,
7446// cx: &mut gpui::TestAppContext,
7447// ) {
7448// init_test(cx, |settings| {
7449// settings
7450// .copilot
7451// .get_or_insert(Default::default())
7452// .disabled_globs = Some(vec![".env*".to_string()]);
7453// });
7454
7455// let (copilot, copilot_lsp) = Copilot::fake(cx);
7456// cx.update(|cx| cx.set_global(copilot));
7457
7458// let fs = FakeFs::new(cx.background());
7459// fs.insert_tree(
7460// "/test",
7461// json!({
7462// ".env": "SECRET=something\n",
7463// "README.md": "hello\n"
7464// }),
7465// )
7466// .await;
7467// let project = Project::test(fs, ["/test".as_ref()], cx).await;
7468
7469// let private_buffer = project
7470// .update(cx, |project, cx| {
7471// project.open_local_buffer("/test/.env", cx)
7472// })
7473// .await
7474// .unwrap();
7475// let public_buffer = project
7476// .update(cx, |project, cx| {
7477// project.open_local_buffer("/test/README.md", cx)
7478// })
7479// .await
7480// .unwrap();
7481
7482// let multibuffer = cx.add_model(|cx| {
7483// let mut multibuffer = MultiBuffer::new(0);
7484// multibuffer.push_excerpts(
7485// private_buffer.clone(),
7486// [ExcerptRange {
7487// context: Point::new(0, 0)..Point::new(1, 0),
7488// primary: None,
7489// }],
7490// cx,
7491// );
7492// multibuffer.push_excerpts(
7493// public_buffer.clone(),
7494// [ExcerptRange {
7495// context: Point::new(0, 0)..Point::new(1, 0),
7496// primary: None,
7497// }],
7498// cx,
7499// );
7500// multibuffer
7501// });
7502// let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7503
7504// let mut copilot_requests = copilot_lsp
7505// .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7506// Ok(copilot::request::GetCompletionsResult {
7507// completions: vec![copilot::request::Completion {
7508// text: "next line".into(),
7509// range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7510// ..Default::default()
7511// }],
7512// })
7513// });
7514
7515// editor.update(cx, |editor, cx| {
7516// editor.change_selections(None, cx, |selections| {
7517// selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7518// });
7519// editor.next_copilot_suggestion(&Default::default(), cx);
7520// });
7521
7522// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7523// assert!(copilot_requests.try_next().is_err());
7524
7525// editor.update(cx, |editor, cx| {
7526// editor.change_selections(None, cx, |s| {
7527// s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7528// });
7529// editor.next_copilot_suggestion(&Default::default(), cx);
7530// });
7531
7532// deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7533// assert!(copilot_requests.try_next().is_ok());
7534// }
7535
7536// #[gpui::test]
7537// async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7538// init_test(cx, |_| {});
7539
7540// let mut language = Language::new(
7541// LanguageConfig {
7542// name: "Rust".into(),
7543// path_suffixes: vec!["rs".to_string()],
7544// brackets: BracketPairConfig {
7545// pairs: vec![BracketPair {
7546// start: "{".to_string(),
7547// end: "}".to_string(),
7548// close: true,
7549// newline: true,
7550// }],
7551// disabled_scopes_by_bracket_ix: Vec::new(),
7552// },
7553// ..Default::default()
7554// },
7555// Some(tree_sitter_rust::language()),
7556// );
7557// let mut fake_servers = language
7558// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7559// capabilities: lsp::ServerCapabilities {
7560// document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7561// first_trigger_character: "{".to_string(),
7562// more_trigger_character: None,
7563// }),
7564// ..Default::default()
7565// },
7566// ..Default::default()
7567// }))
7568// .await;
7569
7570// let fs = FakeFs::new(cx.background());
7571// fs.insert_tree(
7572// "/a",
7573// json!({
7574// "main.rs": "fn main() { let a = 5; }",
7575// "other.rs": "// Test file",
7576// }),
7577// )
7578// .await;
7579// let project = Project::test(fs, ["/a".as_ref()], cx).await;
7580// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7581// let workspace = cx
7582// .add_window(|cx| Workspace::test_new(project.clone(), cx))
7583// .root(cx);
7584// let worktree_id = workspace.update(cx, |workspace, cx| {
7585// workspace.project().read_with(cx, |project, cx| {
7586// project.worktrees(cx).next().unwrap().read(cx).id()
7587// })
7588// });
7589
7590// let buffer = project
7591// .update(cx, |project, cx| {
7592// project.open_local_buffer("/a/main.rs", cx)
7593// })
7594// .await
7595// .unwrap();
7596// cx.foreground().run_until_parked();
7597// cx.foreground().start_waiting();
7598// let fake_server = fake_servers.next().await.unwrap();
7599// let editor_handle = workspace
7600// .update(cx, |workspace, cx| {
7601// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7602// })
7603// .await
7604// .unwrap()
7605// .downcast::<Editor>()
7606// .unwrap();
7607
7608// fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7609// assert_eq!(
7610// params.text_document_position.text_document.uri,
7611// lsp::Url::from_file_path("/a/main.rs").unwrap(),
7612// );
7613// assert_eq!(
7614// params.text_document_position.position,
7615// lsp::Position::new(0, 21),
7616// );
7617
7618// Ok(Some(vec![lsp::TextEdit {
7619// new_text: "]".to_string(),
7620// range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7621// }]))
7622// });
7623
7624// editor_handle.update(cx, |editor, cx| {
7625// cx.focus(&editor_handle);
7626// editor.change_selections(None, cx, |s| {
7627// s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7628// });
7629// editor.handle_input("{", cx);
7630// });
7631
7632// cx.foreground().run_until_parked();
7633
7634// buffer.read_with(cx, |buffer, _| {
7635// assert_eq!(
7636// buffer.text(),
7637// "fn main() { let a = {5}; }",
7638// "No extra braces from on type formatting should appear in the buffer"
7639// )
7640// });
7641// }
7642
7643// #[gpui::test]
7644// async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7645// init_test(cx, |_| {});
7646
7647// let language_name: Arc<str> = "Rust".into();
7648// let mut language = Language::new(
7649// LanguageConfig {
7650// name: Arc::clone(&language_name),
7651// path_suffixes: vec!["rs".to_string()],
7652// ..Default::default()
7653// },
7654// Some(tree_sitter_rust::language()),
7655// );
7656
7657// let server_restarts = Arc::new(AtomicUsize::new(0));
7658// let closure_restarts = Arc::clone(&server_restarts);
7659// let language_server_name = "test language server";
7660// let mut fake_servers = language
7661// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7662// name: language_server_name,
7663// initialization_options: Some(json!({
7664// "testOptionValue": true
7665// })),
7666// initializer: Some(Box::new(move |fake_server| {
7667// let task_restarts = Arc::clone(&closure_restarts);
7668// fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7669// task_restarts.fetch_add(1, atomic::Ordering::Release);
7670// futures::future::ready(Ok(()))
7671// });
7672// })),
7673// ..Default::default()
7674// }))
7675// .await;
7676
7677// let fs = FakeFs::new(cx.background());
7678// fs.insert_tree(
7679// "/a",
7680// json!({
7681// "main.rs": "fn main() { let a = 5; }",
7682// "other.rs": "// Test file",
7683// }),
7684// )
7685// .await;
7686// let project = Project::test(fs, ["/a".as_ref()], cx).await;
7687// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7688// let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7689// let _buffer = project
7690// .update(cx, |project, cx| {
7691// project.open_local_buffer("/a/main.rs", cx)
7692// })
7693// .await
7694// .unwrap();
7695// let _fake_server = fake_servers.next().await.unwrap();
7696// update_test_language_settings(cx, |language_settings| {
7697// language_settings.languages.insert(
7698// Arc::clone(&language_name),
7699// LanguageSettingsContent {
7700// tab_size: NonZeroU32::new(8),
7701// ..Default::default()
7702// },
7703// );
7704// });
7705// cx.foreground().run_until_parked();
7706// assert_eq!(
7707// server_restarts.load(atomic::Ordering::Acquire),
7708// 0,
7709// "Should not restart LSP server on an unrelated change"
7710// );
7711
7712// update_test_project_settings(cx, |project_settings| {
7713// project_settings.lsp.insert(
7714// "Some other server name".into(),
7715// LspSettings {
7716// initialization_options: Some(json!({
7717// "some other init value": false
7718// })),
7719// },
7720// );
7721// });
7722// cx.foreground().run_until_parked();
7723// assert_eq!(
7724// server_restarts.load(atomic::Ordering::Acquire),
7725// 0,
7726// "Should not restart LSP server on an unrelated LSP settings change"
7727// );
7728
7729// update_test_project_settings(cx, |project_settings| {
7730// project_settings.lsp.insert(
7731// language_server_name.into(),
7732// LspSettings {
7733// initialization_options: Some(json!({
7734// "anotherInitValue": false
7735// })),
7736// },
7737// );
7738// });
7739// cx.foreground().run_until_parked();
7740// assert_eq!(
7741// server_restarts.load(atomic::Ordering::Acquire),
7742// 1,
7743// "Should restart LSP server on a related LSP settings change"
7744// );
7745
7746// update_test_project_settings(cx, |project_settings| {
7747// project_settings.lsp.insert(
7748// language_server_name.into(),
7749// LspSettings {
7750// initialization_options: Some(json!({
7751// "anotherInitValue": false
7752// })),
7753// },
7754// );
7755// });
7756// cx.foreground().run_until_parked();
7757// assert_eq!(
7758// server_restarts.load(atomic::Ordering::Acquire),
7759// 1,
7760// "Should not restart LSP server on a related LSP settings change that is the same"
7761// );
7762
7763// update_test_project_settings(cx, |project_settings| {
7764// project_settings.lsp.insert(
7765// language_server_name.into(),
7766// LspSettings {
7767// initialization_options: None,
7768// },
7769// );
7770// });
7771// cx.foreground().run_until_parked();
7772// assert_eq!(
7773// server_restarts.load(atomic::Ordering::Acquire),
7774// 2,
7775// "Should restart LSP server on another related LSP settings change"
7776// );
7777// }
7778
7779// #[gpui::test]
7780// async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7781// init_test(cx, |_| {});
7782
7783// let mut cx = EditorLspTestContext::new_rust(
7784// lsp::ServerCapabilities {
7785// completion_provider: Some(lsp::CompletionOptions {
7786// trigger_characters: Some(vec![".".to_string()]),
7787// resolve_provider: Some(true),
7788// ..Default::default()
7789// }),
7790// ..Default::default()
7791// },
7792// cx,
7793// )
7794// .await;
7795
7796// cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7797// cx.simulate_keystroke(".");
7798// let completion_item = lsp::CompletionItem {
7799// label: "some".into(),
7800// kind: Some(lsp::CompletionItemKind::SNIPPET),
7801// detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7802// documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7803// kind: lsp::MarkupKind::Markdown,
7804// value: "```rust\nSome(2)\n```".to_string(),
7805// })),
7806// deprecated: Some(false),
7807// sort_text: Some("fffffff2".to_string()),
7808// filter_text: Some("some".to_string()),
7809// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7810// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7811// range: lsp::Range {
7812// start: lsp::Position {
7813// line: 0,
7814// character: 22,
7815// },
7816// end: lsp::Position {
7817// line: 0,
7818// character: 22,
7819// },
7820// },
7821// new_text: "Some(2)".to_string(),
7822// })),
7823// additional_text_edits: Some(vec![lsp::TextEdit {
7824// range: lsp::Range {
7825// start: lsp::Position {
7826// line: 0,
7827// character: 20,
7828// },
7829// end: lsp::Position {
7830// line: 0,
7831// character: 22,
7832// },
7833// },
7834// new_text: "".to_string(),
7835// }]),
7836// ..Default::default()
7837// };
7838
7839// let closure_completion_item = completion_item.clone();
7840// let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7841// let task_completion_item = closure_completion_item.clone();
7842// async move {
7843// Ok(Some(lsp::CompletionResponse::Array(vec![
7844// task_completion_item,
7845// ])))
7846// }
7847// });
7848
7849// request.next().await;
7850
7851// cx.condition(|editor, _| editor.context_menu_visible())
7852// .await;
7853// let apply_additional_edits = cx.update_editor(|editor, cx| {
7854// editor
7855// .confirm_completion(&ConfirmCompletion::default(), cx)
7856// .unwrap()
7857// });
7858// cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7859
7860// cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7861// let task_completion_item = completion_item.clone();
7862// async move { Ok(task_completion_item) }
7863// })
7864// .next()
7865// .await
7866// .unwrap();
7867// apply_additional_edits.await.unwrap();
7868// cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7869// }
7870
7871// #[gpui::test]
7872// async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
7873// init_test(cx, |_| {});
7874
7875// let mut cx = EditorLspTestContext::new(
7876// Language::new(
7877// LanguageConfig {
7878// path_suffixes: vec!["jsx".into()],
7879// overrides: [(
7880// "element".into(),
7881// LanguageConfigOverride {
7882// word_characters: Override::Set(['-'].into_iter().collect()),
7883// ..Default::default()
7884// },
7885// )]
7886// .into_iter()
7887// .collect(),
7888// ..Default::default()
7889// },
7890// Some(tree_sitter_typescript::language_tsx()),
7891// )
7892// .with_override_query("(jsx_self_closing_element) @element")
7893// .unwrap(),
7894// lsp::ServerCapabilities {
7895// completion_provider: Some(lsp::CompletionOptions {
7896// trigger_characters: Some(vec![":".to_string()]),
7897// ..Default::default()
7898// }),
7899// ..Default::default()
7900// },
7901// cx,
7902// )
7903// .await;
7904
7905// cx.lsp
7906// .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
7907// Ok(Some(lsp::CompletionResponse::Array(vec![
7908// lsp::CompletionItem {
7909// label: "bg-blue".into(),
7910// ..Default::default()
7911// },
7912// lsp::CompletionItem {
7913// label: "bg-red".into(),
7914// ..Default::default()
7915// },
7916// lsp::CompletionItem {
7917// label: "bg-yellow".into(),
7918// ..Default::default()
7919// },
7920// ])))
7921// });
7922
7923// cx.set_state(r#"<p class="bgˇ" />"#);
7924
7925// // Trigger completion when typing a dash, because the dash is an extra
7926// // word character in the 'element' scope, which contains the cursor.
7927// cx.simulate_keystroke("-");
7928// cx.foreground().run_until_parked();
7929// cx.update_editor(|editor, _| {
7930// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7931// assert_eq!(
7932// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7933// &["bg-red", "bg-blue", "bg-yellow"]
7934// );
7935// } else {
7936// panic!("expected completion menu to be open");
7937// }
7938// });
7939
7940// cx.simulate_keystroke("l");
7941// cx.foreground().run_until_parked();
7942// cx.update_editor(|editor, _| {
7943// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7944// assert_eq!(
7945// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7946// &["bg-blue", "bg-yellow"]
7947// );
7948// } else {
7949// panic!("expected completion menu to be open");
7950// }
7951// });
7952
7953// // When filtering completions, consider the character after the '-' to
7954// // be the start of a subword.
7955// cx.set_state(r#"<p class="yelˇ" />"#);
7956// cx.simulate_keystroke("l");
7957// cx.foreground().run_until_parked();
7958// cx.update_editor(|editor, _| {
7959// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7960// assert_eq!(
7961// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7962// &["bg-yellow"]
7963// );
7964// } else {
7965// panic!("expected completion menu to be open");
7966// }
7967// });
7968// }
7969
7970// #[gpui::test]
7971// async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
7972// init_test(cx, |settings| {
7973// settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
7974// });
7975
7976// let mut language = Language::new(
7977// LanguageConfig {
7978// name: "Rust".into(),
7979// path_suffixes: vec!["rs".to_string()],
7980// prettier_parser_name: Some("test_parser".to_string()),
7981// ..Default::default()
7982// },
7983// Some(tree_sitter_rust::language()),
7984// );
7985
7986// let test_plugin = "test_plugin";
7987// let _ = language
7988// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7989// prettier_plugins: vec![test_plugin],
7990// ..Default::default()
7991// }))
7992// .await;
7993
7994// let fs = FakeFs::new(cx.background());
7995// fs.insert_file("/file.rs", Default::default()).await;
7996
7997// let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7998// let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
7999// project.update(cx, |project, _| {
8000// project.languages().add(Arc::new(language));
8001// });
8002// let buffer = project
8003// .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8004// .await
8005// .unwrap();
8006
8007// let buffer_text = "one\ntwo\nthree\n";
8008// let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8009// let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
8010// editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8011
8012// let format = editor.update(cx, |editor, cx| {
8013// editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8014// });
8015// format.await.unwrap();
8016// assert_eq!(
8017// editor.read_with(cx, |editor, cx| editor.text(cx)),
8018// buffer_text.to_string() + prettier_format_suffix,
8019// "Test prettier formatting was not applied to the original buffer text",
8020// );
8021
8022// update_test_language_settings(cx, |settings| {
8023// settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8024// });
8025// let format = editor.update(cx, |editor, cx| {
8026// editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8027// });
8028// format.await.unwrap();
8029// assert_eq!(
8030// editor.read_with(cx, |editor, cx| editor.text(cx)),
8031// buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8032// "Autoformatting (via test prettier) was not applied to the original buffer text",
8033// );
8034// }
8035
8036// fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8037// let point = DisplayPoint::new(row as u32, column as u32);
8038// point..point
8039// }
8040
8041// fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8042// let (text, ranges) = marked_text_ranges(marked_text, true);
8043// assert_eq!(view.text(cx), text);
8044// assert_eq!(
8045// view.selections.ranges(cx),
8046// ranges,
8047// "Assert selections are {}",
8048// marked_text
8049// );
8050// }
8051
8052// /// Handle completion request passing a marked string specifying where the completion
8053// /// should be triggered from using '|' character, what range should be replaced, and what completions
8054// /// should be returned using '<' and '>' to delimit the range
8055// pub fn handle_completion_request<'a>(
8056// cx: &mut EditorLspTestContext<'a>,
8057// marked_string: &str,
8058// completions: Vec<&'static str>,
8059// ) -> impl Future<Output = ()> {
8060// let complete_from_marker: TextRangeMarker = '|'.into();
8061// let replace_range_marker: TextRangeMarker = ('<', '>').into();
8062// let (_, mut marked_ranges) = marked_text_ranges_by(
8063// marked_string,
8064// vec![complete_from_marker.clone(), replace_range_marker.clone()],
8065// );
8066
8067// let complete_from_position =
8068// cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8069// let replace_range =
8070// cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8071
8072// let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8073// let completions = completions.clone();
8074// async move {
8075// assert_eq!(params.text_document_position.text_document.uri, url.clone());
8076// assert_eq!(
8077// params.text_document_position.position,
8078// complete_from_position
8079// );
8080// Ok(Some(lsp::CompletionResponse::Array(
8081// completions
8082// .iter()
8083// .map(|completion_text| lsp::CompletionItem {
8084// label: completion_text.to_string(),
8085// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8086// range: replace_range,
8087// new_text: completion_text.to_string(),
8088// })),
8089// ..Default::default()
8090// })
8091// .collect(),
8092// )))
8093// }
8094// });
8095
8096// async move {
8097// request.next().await;
8098// }
8099// }
8100
8101// fn handle_resolve_completion_request<'a>(
8102// cx: &mut EditorLspTestContext<'a>,
8103// edits: Option<Vec<(&'static str, &'static str)>>,
8104// ) -> impl Future<Output = ()> {
8105// let edits = edits.map(|edits| {
8106// edits
8107// .iter()
8108// .map(|(marked_string, new_text)| {
8109// let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8110// let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8111// lsp::TextEdit::new(replace_range, new_text.to_string())
8112// })
8113// .collect::<Vec<_>>()
8114// });
8115
8116// let mut request =
8117// cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8118// let edits = edits.clone();
8119// async move {
8120// Ok(lsp::CompletionItem {
8121// additional_text_edits: edits,
8122// ..Default::default()
8123// })
8124// }
8125// });
8126
8127// async move {
8128// request.next().await;
8129// }
8130// }
8131
8132// fn handle_copilot_completion_request(
8133// lsp: &lsp::FakeLanguageServer,
8134// completions: Vec<copilot::request::Completion>,
8135// completions_cycling: Vec<copilot::request::Completion>,
8136// ) {
8137// lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8138// let completions = completions.clone();
8139// async move {
8140// Ok(copilot::request::GetCompletionsResult {
8141// completions: completions.clone(),
8142// })
8143// }
8144// });
8145// lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8146// let completions_cycling = completions_cycling.clone();
8147// async move {
8148// Ok(copilot::request::GetCompletionsResult {
8149// completions: completions_cycling.clone(),
8150// })
8151// }
8152// });
8153// }
8154
8155// pub(crate) fn update_test_language_settings(
8156// cx: &mut TestAppContext,
8157// f: impl Fn(&mut AllLanguageSettingsContent),
8158// ) {
8159// cx.update(|cx| {
8160// cx.update_global::<SettingsStore, _, _>(|store, cx| {
8161// store.update_user_settings::<AllLanguageSettings>(cx, f);
8162// });
8163// });
8164// }
8165
8166// pub(crate) fn update_test_project_settings(
8167// cx: &mut TestAppContext,
8168// f: impl Fn(&mut ProjectSettings),
8169// ) {
8170// cx.update(|cx| {
8171// cx.update_global::<SettingsStore, _, _>(|store, cx| {
8172// store.update_user_settings::<ProjectSettings>(cx, f);
8173// });
8174// });
8175// }
8176
8177// pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8178// cx.foreground().forbid_parking();
8179
8180// cx.update(|cx| {
8181// cx.set_global(SettingsStore::test(cx));
8182// theme::init((), cx);
8183// client::init_settings(cx);
8184// language::init(cx);
8185// Project::init_settings(cx);
8186// workspace::init_settings(cx);
8187// crate::init(cx);
8188// });
8189
8190// update_test_language_settings(cx, f);
8191// }