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