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