nvim_oxi_api/
window.rs

1use std::error::Error as StdError;
2use std::fmt;
3use std::result::Result as StdResult;
4
5use luajit::{self as lua, Poppable, Pushable};
6use serde::{Deserialize, Serialize};
7use types::{
8    self as nvim,
9    conversion::{self, FromObject, ToObject},
10    Array,
11    Function,
12    Integer,
13    Object,
14    WinHandle,
15};
16
17use crate::choose;
18use crate::ffi::window::*;
19#[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
20use crate::opts::WinTextHeightOpts;
21#[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
22use crate::types::WinTextHeightInfos;
23use crate::Result;
24use crate::{Buffer, IntoResult, TabPage};
25
26/// A wrapper around a Neovim window handle.
27#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
28pub struct Window(pub(crate) WinHandle);
29
30impl fmt::Debug for Window {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        f.debug_tuple("Window").field(&self.0).finish()
33    }
34}
35
36impl fmt::Display for Window {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        fmt::Debug::fmt(self, f)
39    }
40}
41
42impl<H: Into<WinHandle>> From<H> for Window {
43    fn from(handle: H) -> Self {
44        Self(handle.into())
45    }
46}
47
48impl From<Window> for Object {
49    fn from(win: Window) -> Self {
50        win.0.into()
51    }
52}
53
54impl From<&Window> for Object {
55    fn from(win: &Window) -> Self {
56        win.0.into()
57    }
58}
59
60impl FromObject for Window {
61    fn from_object(obj: Object) -> StdResult<Self, conversion::Error> {
62        Ok(WinHandle::from_object(obj)?.into())
63    }
64}
65
66impl Poppable for Window {
67    unsafe fn pop(
68        lstate: *mut lua::ffi::lua_State,
69    ) -> std::result::Result<Self, lua::Error> {
70        WinHandle::pop(lstate).map(Into::into)
71    }
72}
73
74impl Pushable for Window {
75    unsafe fn push(
76        self,
77        lstate: *mut lua::ffi::lua_State,
78    ) -> std::result::Result<std::ffi::c_int, lua::Error> {
79        self.0.push(lstate)
80    }
81}
82
83impl Window {
84    /// Shorthand for [`get_current_win`](crate::get_current_win).
85    #[inline(always)]
86    pub fn current() -> Self {
87        crate::get_current_win()
88    }
89
90    /// Retrieve window's underlying id/handle
91    #[inline(always)]
92    pub fn handle(&self) -> i32 {
93        self.0
94    }
95
96    /// Binding to [`nvim_win_call()`][1].
97    ///
98    /// Calls a function with this window as the temporary current window.
99    ///
100    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_call()
101    pub fn call<F, Res, Ret>(&self, fun: F) -> Result<Ret>
102    where
103        F: FnOnce(()) -> Res + 'static,
104        Res: IntoResult<Ret>,
105        Res::Error: StdError + 'static,
106        Ret: Pushable + FromObject,
107    {
108        let fun = Function::from_fn_once(fun);
109        let mut err = nvim::Error::new();
110        let obj = unsafe { nvim_win_call(self.0, fun.lua_ref(), &mut err) };
111
112        choose!(err, {
113            fun.remove_from_lua_registry();
114            Ok(Ret::from_object(obj)?)
115        })
116    }
117
118    /// Binding to [`nvim_win_close()`][1].
119    ///
120    /// Closes the window. Not allowed when
121    /// [`textlock`](https://neovim.io/doc/user/eval.html#textlock) is active.
122    ///
123    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_close()
124    pub fn close(self, force: bool) -> Result<()> {
125        let mut err = nvim::Error::new();
126        unsafe { nvim_win_close(self.0, force, &mut err) };
127        choose!(err, ())
128    }
129
130    /// Binding to [`nvim_win_del_var()`][1].
131    ///
132    /// Removes a window-scoped (`w:`) variable.
133    ///
134    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_del_var()
135    pub fn del_var(&mut self, name: &str) -> Result<()> {
136        let mut err = nvim::Error::new();
137        let name = nvim::String::from(name);
138        unsafe { nvim_win_del_var(self.0, name.non_owning(), &mut err) };
139        choose!(err, ())
140    }
141
142    /// Binding to [`nvim_win_get_buf()`][1].
143    ///
144    /// Gets the current [`Buffer`] in the window.
145    ///
146    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_buf()
147    pub fn get_buf(&self) -> Result<Buffer> {
148        let mut err = nvim::Error::new();
149        let handle = unsafe { nvim_win_get_buf(self.0, &mut err) };
150        choose!(err, Ok(handle.into()))
151    }
152
153    /// Binding to [`nvim_win_get_cursor()`][1].
154    ///
155    /// Gets the (1,0)-indexed cursor position in the window.
156    ///
157    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_cursor()
158    pub fn get_cursor(&self) -> Result<(usize, usize)> {
159        let mut err = nvim::Error::new();
160        let arr = unsafe {
161            nvim_win_get_cursor(
162                self.0,
163                #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
164                types::arena(),
165                &mut err,
166            )
167        };
168        choose!(err, {
169            let mut iter = arr.into_iter();
170            let line = usize::from_object(iter.next().unwrap())?;
171            let col = usize::from_object(iter.next().unwrap())?;
172            Ok((line, col))
173        })
174    }
175
176    /// Binding to [`nvim_win_get_height()`][1].
177    ///
178    /// Gets the window height as a count of rows.
179    ///
180    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_height()
181    pub fn get_height(&self) -> Result<u32> {
182        let mut err = nvim::Error::new();
183        let height = unsafe { nvim_win_get_height(self.0, &mut err) };
184        choose!(err, Ok(height.try_into().expect("always positive")))
185    }
186
187    /// Binding to [`nvim_win_get_number()`][1].
188    ///
189    /// Gets the window number.
190    ///
191    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_number()
192    pub fn get_number(&self) -> Result<u32> {
193        let mut err = nvim::Error::new();
194        let nr = unsafe { nvim_win_get_number(self.0, &mut err) };
195        choose!(err, Ok(nr.try_into().expect("always positive")))
196    }
197
198    /// Binding to [`nvim_win_get_position()`][1].
199    ///
200    /// Gets the window position in display cells.
201    ///
202    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_position()
203    pub fn get_position(&self) -> Result<(usize, usize)> {
204        let mut err = nvim::Error::new();
205        let arr = unsafe {
206            nvim_win_get_position(
207                self.0,
208                #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
209                types::arena(),
210                &mut err,
211            )
212        };
213        choose!(err, {
214            let mut iter = arr.into_iter();
215            let line = usize::from_object(iter.next().unwrap())?;
216            let col = usize::from_object(iter.next().unwrap())?;
217            Ok((line, col))
218        })
219    }
220
221    /// Binding to [`nvim_win_get_tabpage()`][1].
222    ///
223    /// Gets the window's `TabPage`.
224    ///
225    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_tabpage()
226    pub fn get_tabpage(&self) -> Result<TabPage> {
227        let mut err = nvim::Error::new();
228        let handle = unsafe { nvim_win_get_tabpage(self.0, &mut err) };
229        choose!(err, Ok(handle.into()))
230    }
231
232    /// Binding to [`nvim_win_get_var()`][1].
233    ///
234    /// Gets a window-scoped (`w:`) variable.
235    ///
236    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_var()
237    pub fn get_var<Var>(&self, name: &str) -> Result<Var>
238    where
239        Var: FromObject,
240    {
241        let mut err = nvim::Error::new();
242        let name = nvim::String::from(name);
243        let obj = unsafe {
244            nvim_win_get_var(
245                self.0,
246                name.non_owning(),
247                #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
248                types::arena(),
249                &mut err,
250            )
251        };
252        choose!(err, Ok(Var::from_object(obj)?))
253    }
254
255    /// Binding to [`nvim_win_get_width()`][1].
256    ///
257    /// Gets the window width as a number of columns.
258    ///
259    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_get_width()
260    pub fn get_width(&self) -> Result<u32> {
261        let mut err = nvim::Error::new();
262        let width = unsafe { nvim_win_get_width(self.0, &mut err) };
263        choose!(err, Ok(width.try_into().expect("always positive")))
264    }
265
266    /// Binding to [`nvim_win_hide()`][1].
267    ///
268    /// Closes the window and hides the buffer it contains.
269    ///
270    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_hide()
271    pub fn hide(self) -> Result<()> {
272        let mut err = nvim::Error::new();
273        unsafe { nvim_win_hide(self.0, &mut err) };
274        choose!(err, ())
275    }
276
277    /// Binding to [`nvim_win_is_valid()`][1].
278    ///
279    /// Checks if the window is valid.
280    ///
281    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_is_valid()
282    pub fn is_valid(&self) -> bool {
283        unsafe { nvim_win_is_valid(self.0) }
284    }
285
286    /// Binding to [`nvim_win_set_buf()`][1].
287    ///
288    /// Sets `buffer` as the current buffer in the window.
289    ///
290    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_buf()
291    pub fn set_buf(&mut self, buffer: &Buffer) -> Result<()> {
292        let mut err = nvim::Error::new();
293        unsafe { nvim_win_set_buf(self.0, buffer.0, &mut err) };
294        choose!(err, ())
295    }
296
297    /// Binding to [`nvim_win_set_cursor()`][1].
298    ///
299    /// Sets the (1,0)-indexed cursor in the window. This will scroll the
300    /// window even if it's not the current one.
301    ///
302    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_cursor()
303    pub fn set_cursor(&mut self, line: usize, col: usize) -> Result<()> {
304        let mut err = nvim::Error::new();
305        let pos = Array::from_iter([line as Integer, col as Integer]);
306        unsafe { nvim_win_set_cursor(self.0, pos.non_owning(), &mut err) };
307        choose!(err, ())
308    }
309
310    /// Binding to [`nvim_win_set_height()`][1].
311    ///
312    /// Sets the window height.
313    ///
314    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_height()
315    pub fn set_height(&mut self, height: u32) -> Result<()> {
316        let mut err = nvim::Error::new();
317        unsafe { nvim_win_set_height(self.0, height.into(), &mut err) };
318        choose!(err, ())
319    }
320
321    /// Binding to [`nvim_win_set_hl()`][1].
322    ///
323    /// Sets the highlight namespace for this window. This will the highlights
324    /// defined with [`set_hl`](crate::set_hl) for the given namespace, but
325    /// fall back to global highlights (`ns_id = 0`) if those are missing.
326    ///
327    /// This takes precedence over the `winhighlight` option.
328    ///
329    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_hl()
330    #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
331    #[cfg_attr(
332        docsrs,
333        doc(cfg(any(feature = "neovim-0-10", feature = "neovim-nightly")))
334    )]
335    pub fn set_hl(&mut self, ns_id: u32) -> Result<()> {
336        let mut err = nvim::Error::new();
337        unsafe { nvim_win_set_hl(self.0, ns_id.into(), &mut err) };
338        choose!(err, ())
339    }
340
341    /// Binding to [`nvim_win_set_var()`][1].
342    ///
343    /// Sets a window-scoped (`w:`) variable.
344    ///
345    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_var()
346    pub fn set_var<Var>(&mut self, name: &str, value: Var) -> Result<()>
347    where
348        Var: ToObject,
349    {
350        let mut err = nvim::Error::new();
351        let name = nvim::String::from(name);
352        unsafe {
353            nvim_win_set_var(
354                self.0,
355                name.non_owning(),
356                value.to_object()?.non_owning(),
357                &mut err,
358            )
359        };
360        choose!(err, ())
361    }
362
363    /// Binding to [`nvim_win_set_width()`][1].
364    ///
365    /// Sets the window width.
366    ///
367    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_set_width()
368    pub fn set_width(&mut self, width: u32) -> Result<()> {
369        let mut err = nvim::Error::new();
370        unsafe { nvim_win_set_width(self.0, width.into(), &mut err) };
371        choose!(err, ())
372    }
373
374    /// Binding to [`nvim_win_text_height()`][1].
375    ///
376    /// Computes the number of screen lines occupied by a range of text in a
377    /// given window. Works for off-screen text and takes folds into account.
378    ///
379    /// [1]: https://neovim.io/doc/user/api.html#nvim_win_text_height()
380    #[cfg(feature = "neovim-0-10")] // On 0.10 and nightly.
381    #[cfg_attr(
382        docsrs,
383        doc(cfg(any(feature = "neovim-0-10", feature = "neovim-nightly")))
384    )]
385    pub fn text_height(
386        &self,
387        opts: &WinTextHeightOpts,
388    ) -> Result<WinTextHeightInfos> {
389        let mut err = nvim::Error::new();
390        let dict = unsafe {
391            nvim_win_text_height(self.0, opts, types::arena(), &mut err)
392        };
393        choose!(err, dict.try_into().map_err(Into::into))
394    }
395}