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