diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e3c7223..ae2825db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ - renamed the `lua_` types to ``; +- the argument of `SetHighlightOptsBuilder::{link,global_link}()` from `&str` + to any type implementing `HlGroup`; + ## [0.5.1] - June 23 2024 ### Added diff --git a/crates/api/src/ffi/command.rs b/crates/api/src/ffi/command.rs index d03546ff..b94ecefd 100644 --- a/crates/api/src/ffi/command.rs +++ b/crates/api/src/ffi/command.rs @@ -6,7 +6,7 @@ use crate::opts::*; pub(crate) type ParseCmdOutput = Dictionary; #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly. -pub(crate) type ParseCmdOutput = crate::types::KeyDict_cmd; +pub(crate) type ParseCmdOutput = crate::types::ParseCmdOutput; #[cfg_attr( all(target_os = "windows", target_env = "msvc"), @@ -42,7 +42,7 @@ extern "C" { // https://github.com/neovim/neovim/blob/v0.10.0/src/nvim/api/command.c#L308 pub(crate) fn nvim_cmd( channel_id: u64, - cmd: *const crate::types::KeyDict_cmd, + cmd: *const crate::types::ParseCmdOutput, opts: *const CmdOpts, #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly. arena: *mut Arena, diff --git a/crates/api/src/opts/create_augroup.rs b/crates/api/src/opts/create_augroup.rs index ea2c316d..88d0da7d 100644 --- a/crates/api/src/opts/create_augroup.rs +++ b/crates/api/src/opts/create_augroup.rs @@ -1,10 +1,25 @@ /// Options passed to [`create_augroup()`](crate::create_augroup). +#[cfg(feature = "neovim-nightly")] // Only on Nightly. +#[derive(Clone, Debug, Default, macros::OptsBuilder)] +#[repr(C)] +pub struct CreateAugroupOpts { + #[builder(mask)] + mask: u64, + + /// Whether to clear existing commands if the group already exists. + #[builder(argtype = "bool")] + clear: types::Boolean, +} + +/// Options passed to [`create_augroup()`](crate::create_augroup). +#[cfg(not(feature = "neovim-nightly"))] // On 0.9 and 0.10. #[derive(Clone, Debug, Default)] #[repr(C)] pub struct CreateAugroupOpts { clear: types::Object, } +#[cfg(not(feature = "neovim-nightly"))] // On 0.9 and 0.10. impl CreateAugroupOpts { #[inline(always)] pub fn builder() -> CreateAugroupOptsBuilder { @@ -12,9 +27,11 @@ impl CreateAugroupOpts { } } +#[cfg(not(feature = "neovim-nightly"))] // On 0.9 and 0.10. #[derive(Clone, Default)] pub struct CreateAugroupOptsBuilder(CreateAugroupOpts); +#[cfg(not(feature = "neovim-nightly"))] // On 0.9 and 0.10. impl CreateAugroupOptsBuilder { /// Whether to clear existing commands if the group already exists. #[inline] diff --git a/crates/api/src/opts/set_highlight.rs b/crates/api/src/opts/set_highlight.rs index b5fa4c72..213c13c4 100644 --- a/crates/api/src/opts/set_highlight.rs +++ b/crates/api/src/opts/set_highlight.rs @@ -78,11 +78,19 @@ pub struct SetHighlightOpts { #[builder(skip)] sp: Object, - #[builder(argtype = "&str", inline = "types::String::from({0}).into()")] - link: Object, - - #[builder(argtype = "&str", inline = "types::String::from({0}).into()")] - global_link: Object, + #[builder( + generics = "Hl: crate::HlGroup", + argtype = "Hl", + inline = r#"{ let Ok(hl_id) = {0}.to_hl_id() else { return self; }; hl_id }"# + )] + link: types::HlGroupId, + + #[builder( + generics = "Hl: crate::HlGroup", + argtype = "Hl", + inline = r#"{ let Ok(hl_id) = {0}.to_hl_id() else { return self; }; hl_id }"# + )] + global_link: types::HlGroupId, #[builder(argtype = "bool")] fallback: Boolean, diff --git a/crates/api/src/types/cmd_infos.rs b/crates/api/src/types/cmd_infos.rs index 19be4da3..0103d93d 100644 --- a/crates/api/src/types/cmd_infos.rs +++ b/crates/api/src/types/cmd_infos.rs @@ -168,7 +168,7 @@ impl FromObject for CmdInfos { #[derive(Default, Debug)] #[allow(non_camel_case_types)] #[repr(C)] -pub(crate) struct KeyDict_cmd { +pub(crate) struct ParseCmdOutput { cmd: Object, reg: Object, bang: Object, @@ -183,47 +183,39 @@ pub(crate) struct KeyDict_cmd { } #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly. -#[derive(Default, Debug)] -#[allow(non_camel_case_types)] +#[derive(Default, Debug, Clone, macros::OptsBuilder)] #[repr(C)] -pub(crate) struct KeyDict_cmd { +pub(crate) struct ParseCmdOutput { + #[builder(mask)] mask: u64, - - /// 1st in the mask. cmd: NvimString, - - /// 10th in the mask. range: Array, - - /// 7th in the mask. count: Integer, - - /// 2nd in the mask. reg: NvimString, - - /// 3rd in the mask. bang: Boolean, - - /// 6th in the mask. args: Array, - - /// 8th in the mask. magic: Dictionary, - - /// 5th in the mask. mods: Dictionary, - - /// 9th in the mask. nargs: Object, - /// 4th in the mask. + // Only on 0.10. + #[cfg(all(feature = "neovim-0-10", not(feature = "neovim-nightly")))] addr: Object, - /// 11th in the mask. + // Only on Nightly. + #[cfg(feature = "neovim-nightly")] + addr: NvimString, + + // Only on Nightly. + #[cfg(all(feature = "neovim-0-10", not(feature = "neovim-nightly")))] nextcmd: Object, + + // Only on 0.10. + #[cfg(feature = "neovim-nightly")] + nextcmd: NvimString, } -impl From<&CmdInfos> for KeyDict_cmd { +impl From<&CmdInfos> for ParseCmdOutput { #[inline] fn from(infos: &CmdInfos) -> Self { #[cfg(not(feature = "neovim-0-10"))] // 0nly on 0.9. @@ -256,110 +248,64 @@ impl From<&CmdInfos> for KeyDict_cmd { } #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly. { - let mut mask = 0; + let mut builder = Self::builder(); - let cmd = if let Some(cmd) = infos.cmd.as_deref() { - mask |= 0b11; - NvimString::from(cmd) - } else { - NvimString::default() - }; + if let Some(cmd) = infos.cmd.as_deref() { + builder.cmd(cmd.into()); + } - let range = if let Some(range) = infos.range { - mask |= 0b10000000001; - Array::from(range) - } else { - Array::default() - }; + if let Some(range) = infos.range { + builder.range(Array::from(range)); + } - let count = if let Some(count) = infos.count { - mask |= 0b10000001; - count as Integer - } else { - Integer::default() - }; + if let Some(count) = infos.count { + builder.count(count as Integer); + } - let reg = if let Some(reg) = infos.reg { - mask |= 0b101; - reg.into() - } else { - NvimString::default() - }; + if let Some(reg) = infos.reg { + builder.reg(reg.into()); + } - let bang = if let Some(bang) = infos.bang { - mask |= 0b1001; - bang - } else { - Boolean::default() - }; + if let Some(bang) = infos.bang { + builder.bang(bang); + } - let args = if !infos.args.is_empty() { - mask |= 0b1000001; - Array::from_iter(infos.args.clone()) - } else { - Array::default() - }; + if !infos.args.is_empty() { + builder.args(Array::from_iter(infos.args.clone())); + } - let magic = if let Some(magic) = infos.magic { - mask |= 0b100000001; - Dictionary::from(magic) - } else { - Dictionary::default() - }; + if let Some(magic) = infos.magic { + builder.magic(Dictionary::from(magic)); + } - let mods = if let Some(mods) = infos.mods { - mask |= 0b100001; - Dictionary::from(mods) - } else { - Dictionary::default() - }; + if let Some(mods) = infos.mods { + builder.mods(Dictionary::from(mods)); + } - let nargs = if let Some(nargs) = infos.nargs { - mask |= 0b1000000001; - nargs.to_object().unwrap() - } else { - Object::default() - }; + if let Some(nargs) = infos.nargs { + builder.nargs(nargs.to_object().unwrap()); + } - let addr = if let Some(addr) = infos.addr { - mask |= 0b10001; - addr.to_object().unwrap() - } else { - Object::default() - }; + if let Some(addr) = infos.addr { + builder.addr(addr.as_str().into()); + } - let nextcmd = if let Some(nextcmd) = infos.nextcmd.as_deref() { - mask |= 0b100000000001; - NvimString::from(nextcmd).into() - } else { - Object::default() + if let Some(nextcmd) = infos.nextcmd.as_deref() { + builder.nextcmd(nextcmd.into()); }; - Self { - mask, - cmd, - reg, - bang, - addr, - mods, - args, - count, - magic, - nargs, - range, - nextcmd, - } + builder.build() } } } #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly. -impl TryFrom for CmdInfos { +impl TryFrom for CmdInfos { type Error = conversion::Error; #[inline] - fn try_from(cmd: KeyDict_cmd) -> Result { - let KeyDict_cmd { + fn try_from(cmd: ParseCmdOutput) -> Result { + let ParseCmdOutput { addr, args, bang, @@ -385,7 +331,7 @@ impl TryFrom for CmdInfos { } Ok(Self { - addr: utils::none_literal_is_none(Deserializer::new(addr))?, + addr: utils::none_literal_is_none(Deserializer::new(addr.into()))?, args: deserialize(args)?, bang: deserialize(bang)?, cmd: deserialize(cmd)?, @@ -393,7 +339,9 @@ impl TryFrom for CmdInfos { magic: deserialize(magic)?, mods: deserialize(mods)?, nargs: deserialize(nargs)?, - nextcmd: utils::empty_string_is_none(Deserializer::new(nextcmd))?, + nextcmd: utils::empty_string_is_none(Deserializer::new( + nextcmd.into(), + ))?, range: deserialize(range)?, reg: utils::char_from_string(Deserializer::new(reg.into()))?, }) diff --git a/crates/api/src/types/command_addr.rs b/crates/api/src/types/command_addr.rs index 7ed2417c..d84d608e 100644 --- a/crates/api/src/types/command_addr.rs +++ b/crates/api/src/types/command_addr.rs @@ -20,6 +20,22 @@ pub enum CommandAddr { Other, } +impl CommandAddr { + #[cfg(feature = "neovim-0-10")] // On 0.10 and Nightly. + pub(crate) const fn as_str(&self) -> &'static str { + match self { + Self::Lines => "lines", + Self::Arguments => "arguments", + Self::Buffers => "buffers", + Self::LoadedBuffers => "loaded_buffers", + Self::Windows => "windows", + Self::Tabs => "tabs", + Self::Quickfix => "quickfix", + Self::Other => "other", + } + } +} + impl ToObject for CommandAddr { fn to_object(self) -> Result { self.serialize(Serializer::new()).map_err(Into::into) diff --git a/src/tests.rs b/src/tests.rs index 0bf00cbc..2a25c7e7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -135,11 +135,20 @@ pub enum TestFailure<'a, E> { } fn exit(result: Result<(), Failure>) { + #[cfg(all(feature = "neovim-0-9", not(feature = "neovim-0-10")))] + let exec = |cmd: &str| crate::api::exec(cmd, false).unwrap(); + + #[cfg(feature = "neovim-0-10")] + let exec = |cmd: &str| { + let opts = crate::api::opts::ExecOpts::builder().output(false).build(); + crate::api::exec2(cmd, &opts).unwrap(); + }; + if let Err(failure) = result { eprintln!("{failure}"); - crate::api::exec("cquit 1", false).unwrap(); + exec("cquit 1"); } else { - crate::api::exec("qall!", false).unwrap(); + exec("qall!"); } } diff --git a/tests/src/api/autocmd.rs b/tests/src/api/autocmd.rs index f2d028e3..3a7ad084 100644 --- a/tests/src/api/autocmd.rs +++ b/tests/src/api/autocmd.rs @@ -1,8 +1,7 @@ use all_asserts::*; -use nvim_oxi as oxi; use nvim_oxi::api::{self, opts::*, Buffer}; -#[oxi::test] +#[nvim_oxi::test] fn clear_autocmds_current_buf() { let opts = ClearAutocmdsOpts::builder().buffer(0.into()).build(); assert_eq!(Ok(()), api::clear_autocmds(&opts)); @@ -11,7 +10,7 @@ fn clear_autocmds_current_buf() { assert_eq!(Ok(()), api::clear_autocmds(&opts)); } -#[oxi::test] +#[nvim_oxi::test] fn clear_autocmds_events() { let opts = ClearAutocmdsOpts::builder() .events(["BufFilePre", "BufFilePost"]) @@ -29,7 +28,7 @@ fn clear_autocmds_events() { assert_eq!(Ok(()), api::clear_autocmds(&opts)); } -#[oxi::test] +#[nvim_oxi::test] fn clear_autocmds_buffer_n_patterns() { let opts = ClearAutocmdsOpts::builder() .buffer(0.into()) @@ -42,7 +41,7 @@ fn clear_autocmds_buffer_n_patterns() { ); } -#[oxi::test] +#[nvim_oxi::test] fn create_augroup() { let opts = CreateAugroupOpts::builder().build(); let id = api::create_augroup("Foo", &opts).expect("create_augroup failed"); @@ -53,19 +52,19 @@ fn create_augroup() { assert_eq!(Ok(id), got); } -#[oxi::test] +#[nvim_oxi::test] fn create_autocmd() { let opts = CreateAutocmdOpts::builder() .buffer(0.into()) .desc("Does nothing, in the current buffer") - .callback(|_args| Ok::<_, oxi::Error>(false)) + .callback(|_args| Ok::<_, nvim_oxi::Error>(false)) .build(); let id = api::create_autocmd(["VimEnter"], &opts); assert!(id.is_ok(), "{id:?}"); } -#[oxi::test] +#[nvim_oxi::test] fn create_autocmd_buffer_n_patterns() { let opts = CreateAutocmdOpts::builder() .command("echo 'hi there'") @@ -77,7 +76,7 @@ fn create_autocmd_buffer_n_patterns() { assert!(id.is_err(), "{id:?}"); } -#[oxi::test] +#[nvim_oxi::test] fn exec_autocmds() { use std::cell::RefCell; use std::rc::Rc; @@ -90,7 +89,7 @@ fn exec_autocmds() { .callback(move |_args| { let mut i = cloned.borrow_mut(); *i += 1; - Ok::<_, oxi::Error>(false) + Ok::<_, nvim_oxi::Error>(false) }) .buffer(Buffer::current()) .once(true) @@ -111,31 +110,31 @@ fn exec_autocmds() { assert_eq!(1, *i.try_borrow().unwrap()); } -#[oxi::test] +#[nvim_oxi::test] fn get_autocmds() { let autocmds = api::get_autocmds(&Default::default()).expect("couldn't get autocmds"); assert_lt!(0, autocmds.collect::>().len()); } -#[oxi::test] +#[nvim_oxi::test] fn set_del_augroup_by_id() { let id = api::create_augroup("Foo", &Default::default()) .expect("create_augroup failed"); assert_eq!(Ok(()), api::del_augroup_by_id(id)); } -#[oxi::test] +#[nvim_oxi::test] fn set_del_augroup_by_name() { let _ = api::create_augroup("Foo", &Default::default()) .expect("create_augroup failed"); assert_eq!(Ok(()), api::del_augroup_by_name("Foo")); } -#[oxi::test] +#[nvim_oxi::test] fn set_exec_del_autocmd() { let opts = CreateAutocmdOpts::builder() - .callback(|_args| Ok::<_, oxi::Error>(false)) + .callback(|_args| Ok::<_, nvim_oxi::Error>(false)) .build(); let id = api::create_autocmd(["BufAdd", "BufDelete"], &opts)