nvim_oxi_macros/lib.rs
1use proc_macro::TokenStream;
2use syn::parse_macro_input;
3
4mod common;
5mod derive_opts;
6
7#[cfg(feature = "plugin")]
8mod plugin;
9
10#[cfg(feature = "test")]
11mod test;
12
13/// TODO: docs
14#[proc_macro_derive(OptsBuilder, attributes(builder))]
15pub fn derive_opts_builder(input: TokenStream) -> TokenStream {
16 let input = parse_macro_input!(input as syn::DeriveInput);
17 derive_opts::expand_derive_opts_builder(&input)
18 .unwrap_or_else(syn::Error::into_compile_error)
19 .into()
20}
21
22/// Marks a function as the entrypoint of the plugin.
23///
24/// The function wrapped by this macro will be called by Neovim when the user
25/// loads the plugin by passing its name to the `require` function. It can
26/// return any type that implements the [`Pushable`] trait, and the value will
27/// be returned on the Lua side by `require`.
28///
29/// # Examples
30///
31/// Let's say your crate only consists of this single `lib.rs` file:
32///
33/// ```ignore
34/// // lib.rs
35///
36/// #[nvim_oxi::plugin]
37/// fn my_plugin() -> u32 {
38/// 42
39/// }
40/// ```
41///
42/// Once the crate is compiled and the resulting dynamic library is placed
43/// under `lua/my_plugin.{so|dylib|dll}` somewhere in Neovim's [`runtimepath`],
44/// it can be loaded with:
45///
46/// ```lua
47/// local ret = require("my_plugin")
48/// assert(ret == 42)
49/// ```
50///
51/// [`Pushable`]: https://docs.rs/nvim-oxi/latest/nvim_oxi/lua/trait.Pushable.html
52/// [`runtimepath`]: https://neovim.io/doc/user/options.html#'runtimepath'
53///
54/// # Attributes
55///
56/// ## `nvim-oxi`
57///
58/// The code generated by this macro includes calls to functions defined in the
59/// `nvim-oxi` crate, which is expected to be in scope under `::nvim_oxi`. This
60/// can cause problems if you renamed the crate in your `Cargo.toml` or if it's
61/// re-exported from another crate.
62///
63/// In these cases, you can use the `nvim_oxi` attribute to specify the path to
64/// `nvim-oxi`.
65///
66/// For example, let's say your crate has a single dependency called `foo`
67/// whose whose `lib.rs` re-exports `nvim-oxi` as `nvim`:
68///
69/// ```ignore
70/// // foo's lib.rs
71/// pub use nvim_oxi as nvim;
72/// ```
73///
74/// Doing this would generate a compilation error because `nvim_oxi` is not in
75/// scope:
76///
77/// ```compile_fail
78/// #[foo::nvim::plugin]
79/// fn my_plugin() {}
80/// ```
81///
82/// To fix this, you can use the `nvim_oxi` attribute to specify the correct
83/// path:
84///
85/// ```ignore
86/// #[foo::nvim::plugin(nvim_oxi = foo::nvim)]
87/// fn my_plugin() {}
88/// ```
89#[cfg(feature = "plugin")]
90#[proc_macro_attribute]
91pub fn plugin(attr: TokenStream, item: TokenStream) -> TokenStream {
92 plugin::plugin(attr, item)
93}
94
95/// Tests a piece of code from inside Neovim.
96///
97/// # Examples
98///
99/// ```ignore
100/// use nvim_oxi::api;
101///
102/// #[nvim_oxi::test]
103/// fn set_get_del_var() {
104/// api::set_var("foo", 42).unwrap();
105/// assert_eq!(Ok(42), api::get_var("foo"));
106/// assert_eq!(Ok(()), api::del_var("foo"));
107/// }
108/// ```
109///
110/// The test function can also return a `Result<(), T>` if `T` implements
111/// `Debug`:
112///
113/// ```ignore
114/// # use nvim_oxi::api;
115/// #[nvim_oxi::test]
116/// fn print_42() -> Result<(), api::Error> {
117/// api::command("lua print(42)")
118/// }
119/// ```
120///
121/// # Attributes
122///
123/// ## `nvim-oxi`
124///
125/// Exactly the same as the `nvim-oxi` attribute on the [`macro@plugin`] macro.
126/// See [its documentation](macro@plugin#nvim-oxi) for more information.
127///
128/// ## `cmd`
129///
130/// The `cmd` attribute is used to specify an Ex command that will be executed
131/// by Neovim before the test's body. This can be useful to configure the
132/// environment in which the test will run.
133///
134/// ```ignore
135/// # use nvim_oxi::api;
136/// #[nvim_oxi::test(cmd = "lua print('The answer is...')")]
137/// fn print_42() -> Result<(), api::Error> {
138/// api::command("lua print(42)")
139/// }
140/// ```
141///
142/// If the given string spans multiple lines, it will be joined into a single
143/// line using `;` as the separator.
144/// ```
145#[cfg(feature = "test")]
146#[proc_macro_attribute]
147pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
148 test::test(attr, item)
149}