nvim_api/
extmark.rs

1use std::ops::RangeBounds;
2
3use nvim_types::{
4    self as nvim,
5    conversion::FromObject,
6    Array,
7    Dictionary,
8    Integer,
9};
10
11use crate::choose;
12use crate::ffi::extmark::*;
13use crate::iterator::SuperIterator;
14use crate::opts::*;
15use crate::types::*;
16use crate::utils;
17use crate::Buffer;
18use crate::{Error, Result};
19
20impl Buffer {
21    /// Binding to [`nvim_buf_add_highlight`](https://neovim.io/doc/user/api.html#nvim_buf_add_highlight()).
22    ///
23    /// Adds a highlight to the buffer. Both `line` and `byte_range` are
24    /// 0-indexed.
25    pub fn add_highlight<R>(
26        &mut self,
27        ns_id: u32,
28        hl_group: &str,
29        line: usize,
30        byte_range: R,
31    ) -> Result<i64>
32    where
33        R: RangeBounds<usize>,
34    {
35        let hl_group = nvim::String::from(hl_group);
36        let mut err = nvim::Error::new();
37        let (start, end) = utils::range_to_limits(byte_range);
38        let ns_id = unsafe {
39            nvim_buf_add_highlight(
40                self.0,
41                ns_id.into(),
42                hl_group.non_owning(),
43                line as Integer,
44                start,
45                end,
46                &mut err,
47            )
48        };
49        choose!(err, Ok(ns_id))
50    }
51
52    /// Binding to [`nvim_buf_clear_namespace`](https://neovim.io/doc/user/api.html#nvim_buf_clear_namespace()).
53    ///
54    /// Clears namespaced objects like highlights, extmarks, or virtual text
55    /// from a region.
56    ///
57    /// The line range is 0-indexed.
58    pub fn clear_namespace<R>(
59        &mut self,
60        ns_id: u32,
61        line_range: R,
62    ) -> Result<()>
63    where
64        R: RangeBounds<usize>,
65    {
66        let mut err = nvim::Error::new();
67        let (start, end) = utils::range_to_limits(line_range);
68        unsafe {
69            nvim_buf_clear_namespace(
70                self.0,
71                ns_id as Integer,
72                start,
73                end,
74                &mut err,
75            )
76        };
77        choose!(err, ())
78    }
79
80    /// Binding to [`nvim_buf_del_extmark`][1].
81    ///
82    /// Removes an extmark from the buffer.
83    ///
84    /// [1]: https://neovim.io/doc/user/api.html#nvim_buf_del_extmark()
85    pub fn del_extmark(&mut self, ns_id: u32, extmark_id: u32) -> Result<()> {
86        let mut err = nvim::Error::new();
87        let was_found = unsafe {
88            nvim_buf_del_extmark(
89                self.0,
90                ns_id as Integer,
91                extmark_id as Integer,
92                &mut err,
93            )
94        };
95        choose!(
96            err,
97            match was_found {
98                true => Ok(()),
99                _ => Err(Error::custom(format!(
100                    "No extmark with id {extmark_id} was found"
101                ))),
102            }
103        )
104    }
105
106    /// Binding to [`nvim_buf_get_extmark_by_id`][1].
107    ///
108    /// The first two elements of the returned tuple represent the 0-indexed
109    /// `row, col` position of the extmark. The last element is only present if
110    /// the [`details`](crate::opts::GetExtmarkByIdOptsBuilder::details) option
111    /// field was set to `true`.
112    ///
113    /// [1]: https://neovim.io/doc/user/api.html#nvim_buf_get_extmark_by_id()
114    pub fn get_extmark_by_id(
115        &self,
116        ns_id: u32,
117        extmark_id: u32,
118        opts: &GetExtmarkByIdOpts,
119    ) -> Result<(usize, usize, Option<ExtmarkInfos>)> {
120        let opts = Dictionary::from(opts);
121        let mut err = nvim::Error::new();
122        let tuple = unsafe {
123            nvim_buf_get_extmark_by_id(
124                self.0,
125                ns_id as Integer,
126                extmark_id as Integer,
127                opts.non_owning(),
128                &mut err,
129            )
130        };
131        choose!(err, {
132            if tuple.is_empty() {
133                return Err(Error::custom(format!(
134                    "No extmark with id {extmark_id} was found"
135                )));
136            }
137
138            let mut iter = tuple.into_iter();
139            let row =
140                usize::from_object(iter.next().expect("row is present"))?;
141            let col =
142                usize::from_object(iter.next().expect("col is present"))?;
143            let infos =
144                iter.next().map(ExtmarkInfos::from_object).transpose()?;
145            Ok((row, col, infos))
146        })
147    }
148
149    /// Bindings to [`nvim_buf_get_extmarks`][1].
150    ///
151    /// Gets all the extmarks in a buffer region specified by start and end
152    /// positions. Returns an iterator over `(extmark_id, row, col, infos)`
153    /// tuples in "traversal order". Like for [`Buffer::get_extmark_by_id`],
154    /// the `infos` are present only if the
155    /// [`details`](crate::opts::GetExtmarksOptsBuilder::details) option field
156    /// was set to `true`.
157    ///
158    /// [1]: https://neovim.io/doc/user/api.html#nvim_buf_get_extmarks()
159    pub fn get_extmarks(
160        &self,
161        ns_id: u32,
162        start: ExtmarkPosition,
163        end: ExtmarkPosition,
164        opts: &GetExtmarksOpts,
165    ) -> Result<impl SuperIterator<(u32, usize, usize, Option<ExtmarkInfos>)>>
166    {
167        let opts = Dictionary::from(opts);
168        let mut err = nvim::Error::new();
169        let extmarks = unsafe {
170            nvim_buf_get_extmarks(
171                self.0,
172                ns_id as Integer,
173                start.into(),
174                end.into(),
175                opts.non_owning(),
176                &mut err,
177            )
178        };
179        choose!(
180            err,
181            Ok({
182                extmarks.into_iter().map(|tuple| {
183                    let mut iter =
184                        Array::from_object(tuple).unwrap().into_iter();
185                    let id =
186                        u32::from_object(iter.next().expect("id is present"))
187                            .unwrap();
188                    let row = usize::from_object(
189                        iter.next().expect("row is present"),
190                    )
191                    .unwrap();
192                    let col = usize::from_object(
193                        iter.next().expect("col is present"),
194                    )
195                    .unwrap();
196                    let infos = iter
197                        .next()
198                        .map(ExtmarkInfos::from_object)
199                        .transpose()
200                        .unwrap();
201                    (id, row, col, infos)
202                })
203            })
204        )
205    }
206
207    /// Binding to [`nvim_buf_set_extmark`](https://neovim.io/doc/user/api.html#nvim_buf_set_extmark()).
208    ///
209    /// Creates or updates an extmark. Both `line` and `col` are 0-indexed.
210    /// Returns the id of the created/updated extmark.
211    pub fn set_extmark(
212        &mut self,
213        ns_id: u32,
214        line: usize,
215        col: usize,
216        opts: &SetExtmarkOpts,
217    ) -> Result<u32> {
218        let mut err = nvim::Error::new();
219        let id = unsafe {
220            nvim_buf_set_extmark(
221                self.0,
222                ns_id as Integer,
223                line as Integer,
224                col as Integer,
225                &opts.0,
226                &mut err,
227            )
228        };
229        choose!(err, Ok(id.try_into().expect("always positive")))
230    }
231}
232
233/// Binding to [`nvim_create_namespace`](https://neovim.io/doc/user/api.html#nvim_create_namespace()).
234///
235/// Creates a new namespace or gets the id of an existing one. If `name`
236/// matches an existing namespace the associated id is returned.
237pub fn create_namespace(name: &str) -> u32 {
238    let name = nvim::String::from(name);
239    unsafe { nvim_create_namespace(name.non_owning()) }
240        .try_into()
241        .expect("always positive")
242}
243
244/// Binding to [`nvim_get_namespaces`](https://neovim.io/doc/user/api.html#nvim_get_namespaces()).
245///
246/// Returns an iterator over all the existing, non-anonymous namespace names
247/// and ids tuples `(name, id)`.
248pub fn get_namespaces() -> impl SuperIterator<(String, u32)> {
249    unsafe { nvim_get_namespaces() }.into_iter().map(|(k, v)| {
250        let k = k.try_into().expect("namespace name is valid UTF-8");
251        let v = u32::from_object(v).expect("namespace id is positive");
252        (k, v)
253    })
254}
255
256/// Binding to [`nvim_set_decoration_provider`](https://neovim.io/doc/user/api.html#nvim_set_decoration_provider()).
257///
258/// Sets or changes a decoration provider for a namespace.
259pub fn set_decoration_provider(
260    ns_id: u32,
261    opts: &DecorationProviderOpts,
262) -> Result<()> {
263    let opts = Dictionary::from(opts);
264    let mut err = nvim::Error::new();
265    unsafe {
266        nvim_set_decoration_provider(
267            ns_id as Integer,
268            opts.non_owning(),
269            &mut err,
270        )
271    };
272    choose!(err, ())
273}