//! Completion tests for item list position.
use expect_test::expect;

use crate::tests::{check, check_edit, check_with_base_items};

#[test]
fn in_mod_item_list() {
    check_with_base_items(
        r#"mod tests { $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw struct
            kw super::
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
            sn macro_rules
            sn tfn (Test function)
            sn tmod (Test module)
        "#]],
    )
}

#[test]
fn in_source_file_item_list() {
    check_with_base_items(
        r#"$0"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
            sn macro_rules
            sn tfn (Test function)
            sn tmod (Test module)
        "#]],
    )
}

#[test]
fn in_item_list_after_attr() {
    check_with_base_items(
        r#"#[attr] $0"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
            sn macro_rules
            sn tfn (Test function)
            sn tmod (Test module)
        "#]],
    )
}

#[test]
fn in_item_list_after_inner_attr() {
    check_with_base_items(
        r#"#![attr] $0"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
            sn macro_rules
            sn tfn (Test function)
            sn tmod (Test module)
        "#]],
    )
}

#[test]
fn in_qualified_path() {
    check_with_base_items(
        r#"crate::$0"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
        "#]],
    )
}

#[test]
fn after_unsafe_token() {
    check_with_base_items(
        r#"unsafe $0"#,
        expect![[r#"
            kw async
            kw extern
            kw fn
            kw impl
            kw impl for
            kw trait
        "#]],
    );
}

#[test]
fn after_async_token() {
    check_with_base_items(
        r#"async $0"#,
        expect![[r#"
            kw fn
            kw unsafe
        "#]],
    );
}

#[test]
fn after_visibility() {
    check_with_base_items(
        r#"pub $0"#,
        expect![[r#"
            kw async
            kw const
            kw enum
            kw extern
            kw fn
            kw mod
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
        "#]],
    );
}

#[test]
fn after_visibility_unsafe() {
    check_with_base_items(
        r#"pub unsafe $0"#,
        expect![[r#"
            kw async
            kw fn
            kw trait
        "#]],
    );
}

#[test]
fn after_abi() {
    check_with_base_items(
        r#"extern "C" $0"#,
        expect![[r#"
            kw async
            kw const
            kw enum
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
        "#]],
    );
    check_with_base_items(
        r#"extern "C" f$0"#,
        expect![[r#"
            kw async
            kw const
            kw enum
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
        "#]],
    );
}

#[test]
fn after_extern_token() {
    check_with_base_items(
        r#"extern $0"#,
        expect![[r#"
            kw async
            kw const
            kw crate
            kw enum
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
        "#]],
    );
    check_with_base_items(
        r#"extern cr$0"#,
        expect![[r#"
            kw async
            kw const
            kw crate
            kw enum
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
        "#]],
    );
    check_edit("crate", "extern $0", "extern crate $0;");
}

#[test]
fn in_impl_assoc_item_list() {
    check_with_base_items(
        r#"impl Struct { $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw unsafe
        "#]],
    )
}

#[test]
fn in_impl_assoc_item_list_after_attr() {
    check_with_base_items(
        r#"impl Struct { #[attr] $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw unsafe
        "#]],
    )
}

#[test]
fn in_trait_assoc_item_list() {
    check_with_base_items(
        r"trait Foo { $0 }",
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw self::
            kw type
            kw unsafe
        "#]],
    );
}

#[test]
fn in_trait_assoc_fn_missing_body() {
    check_with_base_items(
        r#"trait Foo { fn function(); $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw self::
            kw type
            kw unsafe
        "#]],
    );
}

#[test]
fn in_trait_assoc_const_missing_body() {
    check_with_base_items(
        r#"trait Foo { const CONST: (); $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw self::
            kw type
            kw unsafe
        "#]],
    );
}

#[test]
fn in_trait_assoc_type_aliases_missing_ty() {
    check_with_base_items(
        r#"trait Foo { type Type; $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw fn
            kw self::
            kw type
            kw unsafe
        "#]],
    );
}

#[test]
fn in_trait_impl_assoc_item_list() {
    check_with_base_items(
        r#"
trait Test {
    type Type0;
    type Type1;
    const CONST0: ();
    const CONST1: ();
    fn function0();
    fn function1();
    async fn function2();
}

impl Test for () {
    type Type0 = ();
    const CONST0: () = ();
    fn function0() {}
    $0
}
"#,
        expect![[r#"
            ct const CONST1: () =
            fn async fn function2()
            fn fn function1()
            fn fn function2()
            ma makro!(…) macro_rules! makro
            md module::
            ta type Type1 =
            kw crate::
            kw self::
        "#]],
    );
}

#[test]
fn in_trait_impl_no_unstable_item_on_stable() {
    check(
        r#"
trait Test {
    #[unstable]
    type Type;
    #[unstable]
    const CONST: ();
    #[unstable]
    fn function();
}

impl Test for () {
    $0
}
"#,
        expect![[r#"
            kw crate::
            kw self::
        "#]],
    );
}

#[test]
fn in_trait_impl_unstable_item_on_nightly() {
    check(
        r#"
//- toolchain:nightly
trait Test {
    #[unstable]
    type Type;
    #[unstable]
    const CONST: ();
    #[unstable]
    fn function();
}

impl Test for () {
    $0
}
"#,
        expect![[r#"
            ct const CONST: () =
            fn fn function()
            ta type Type =
            kw crate::
            kw self::
        "#]],
    );
}

#[test]
fn after_unit_struct() {
    check_with_base_items(
        r#"struct S; f$0"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw fn
            kw impl
            kw impl for
            kw mod
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw struct
            kw trait
            kw type
            kw union
            kw unsafe
            kw use
            sn macro_rules
            sn tfn (Test function)
            sn tmod (Test module)
        "#]],
    );
}

#[test]
fn type_in_impl_trait() {
    check_edit(
        "type O",
        r"
struct A;
trait B {
type O: ?Sized;
}
impl B for A {
$0
}
",
        r#"
struct A;
trait B {
type O: ?Sized;
}
impl B for A {
type O = $0;
}
"#,
    );
    check_edit(
        "type O",
        r"
struct A;
trait B {
type O;
}
impl B for A {
$0
}
",
        r#"
struct A;
trait B {
type O;
}
impl B for A {
type O = $0;
}
"#,
    );
    check_edit(
        "type O",
        r"
struct A;
trait B {
type O<'a>
where
Self: 'a;
}
impl B for A {
$0
}
",
        r#"
struct A;
trait B {
type O<'a>
where
Self: 'a;
}
impl B for A {
type O<'a> = $0
where
Self: 'a;
}
"#,
    );
    check_edit(
        "type O",
        r"
struct A;
trait B {
type O: ?Sized = u32;
}
impl B for A {
$0
}
",
        r#"
struct A;
trait B {
type O: ?Sized = u32;
}
impl B for A {
type O = $0;
}
"#,
    );
    check_edit(
        "type O",
        r"
struct A;
trait B {
type O = u32;
}
impl B for A {
$0
}
",
        r"
struct A;
trait B {
type O = u32;
}
impl B for A {
type O = $0;
}
",
    )
}

#[test]
fn inside_extern_blocks() {
    // Should suggest `fn`, `static`, `unsafe`
    check_with_base_items(
        r#"extern { $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw crate::
            kw fn
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw static
            kw unsafe
        "#]],
    );

    // Should suggest `fn`, `static`, `safe`, `unsafe`
    check_with_base_items(
        r#"unsafe extern { $0 }"#,
        expect![[r#"
            ma makro!(…) macro_rules! makro
            md module::
            kw crate::
            kw fn
            kw pub
            kw pub(crate)
            kw pub(super)
            kw safe
            kw self::
            kw static
            kw unsafe
        "#]],
    );

    check_with_base_items(
        r#"unsafe extern { pub safe $0 }"#,
        expect![[r#"
            kw fn
            kw static
        "#]],
    );

    check_with_base_items(
        r#"unsafe extern { pub unsafe $0 }"#,
        expect![[r#"
            kw fn
            kw static
        "#]],
    )
}

#[test]
fn tokens_from_macro() {
    check_edit(
        "fn as_ref",
        r#"
//- proc_macros: identity
//- minicore: as_ref
struct Foo;

#[proc_macros::identity]
impl<'a> AsRef<&'a i32> for Foo {
    $0
}
    "#,
        r#"
struct Foo;

#[proc_macros::identity]
impl<'a> AsRef<&'a i32> for Foo {
    fn as_ref(&self) -> &&'a i32 {
    $0
}
}
    "#,
    );
}
