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 Array,
10 Function,
11 Integer,
12 Object,
13 WinHandle,
14 conversion::{self, FromObject, ToObject},
15};
16
17use crate::Result;
18use crate::choose;
19use crate::ffi::window::*;
20use crate::opts::WinTextHeightOpts;
21use crate::types::WinTextHeightInfos;
22use crate::{Buffer, IntoResult, TabPage};
23
24#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
26pub struct Window(pub(crate) WinHandle);
27
28impl fmt::Debug for Window {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 f.debug_tuple("Window").field(&self.0).finish()
31 }
32}
33
34impl fmt::Display for Window {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 fmt::Debug::fmt(self, f)
37 }
38}
39
40impl<H: Into<WinHandle>> From<H> for Window {
41 fn from(handle: H) -> Self {
42 Self(handle.into())
43 }
44}
45
46impl From<Window> for Object {
47 fn from(win: Window) -> Self {
48 win.0.into()
49 }
50}
51
52impl From<&Window> for Object {
53 fn from(win: &Window) -> Self {
54 win.0.into()
55 }
56}
57
58impl FromObject for Window {
59 fn from_object(obj: Object) -> StdResult<Self, conversion::Error> {
60 Ok(WinHandle::from_object(obj)?.into())
61 }
62}
63
64impl Poppable for Window {
65 unsafe fn pop(
66 lstate: *mut lua::ffi::State,
67 ) -> std::result::Result<Self, lua::Error> {
68 WinHandle::pop(lstate).map(Into::into)
69 }
70}
71
72impl Pushable for Window {
73 unsafe fn push(
74 self,
75 lstate: *mut lua::ffi::State,
76 ) -> std::result::Result<std::ffi::c_int, lua::Error> {
77 self.0.push(lstate)
78 }
79}
80
81impl Window {
82 #[inline(always)]
84 pub fn current() -> Self {
85 crate::get_current_win()
86 }
87
88 #[inline(always)]
90 pub fn handle(&self) -> i32 {
91 self.0
92 }
93
94 pub fn call<F, Res, Ret>(&self, fun: F) -> Result<Ret>
100 where
101 F: FnOnce(()) -> Res + 'static,
102 Res: IntoResult<Ret>,
103 Res::Error: StdError + 'static,
104 Ret: Pushable + FromObject,
105 {
106 let fun = Function::from_fn_once(fun);
107 let mut err = nvim::Error::new();
108
109 let ref_or_nil =
110 unsafe { nvim_win_call(self.0, fun.lua_ref(), &mut err) };
111
112 let lua_ref = match ref_or_nil.kind() {
113 types::ObjectKind::LuaRef => unsafe {
114 ref_or_nil.as_luaref_unchecked()
115 },
116 types::ObjectKind::Nil => {
117 return Ret::from_object(Object::nil()).map_err(Into::into);
118 },
119 other => panic!("Unexpected object kind: {other:?}"),
120 };
121
122 let obj = unsafe {
123 lua::with_state(|lstate| {
124 lua::ffi::lua_rawgeti(
125 lstate,
126 lua::ffi::LUA_REGISTRYINDEX,
127 lua_ref,
128 );
129 Object::pop(lstate)
130 })
131 }
132 .map_err(crate::Error::custom)?;
133
134 choose!(err, {
135 fun.remove_from_lua_registry();
136 Ok(Ret::from_object(obj)?)
137 })
138 }
139
140 pub fn close(self, force: bool) -> Result<()> {
147 let mut err = nvim::Error::new();
148 unsafe { nvim_win_close(self.0, force, &mut err) };
149 choose!(err, ())
150 }
151
152 pub fn del_var(&mut self, name: &str) -> Result<()> {
158 let mut err = nvim::Error::new();
159 let name = nvim::String::from(name);
160 unsafe { nvim_win_del_var(self.0, name.as_nvim_str(), &mut err) };
161 choose!(err, ())
162 }
163
164 pub fn get_buf(&self) -> Result<Buffer> {
170 let mut err = nvim::Error::new();
171 let handle = unsafe { nvim_win_get_buf(self.0, &mut err) };
172 choose!(err, Ok(handle.into()))
173 }
174
175 pub fn get_cursor(&self) -> Result<(usize, usize)> {
181 let mut err = nvim::Error::new();
182 let arr =
183 unsafe { nvim_win_get_cursor(self.0, types::arena(), &mut err) };
184 choose!(err, {
185 let mut iter = arr.into_iter();
186 let line = usize::from_object(iter.next().unwrap())?;
187 let col = usize::from_object(iter.next().unwrap())?;
188 Ok((line, col))
189 })
190 }
191
192 pub fn get_height(&self) -> Result<u32> {
198 let mut err = nvim::Error::new();
199 let height = unsafe { nvim_win_get_height(self.0, &mut err) };
200 choose!(err, Ok(height.try_into().expect("always positive")))
201 }
202
203 pub fn get_number(&self) -> Result<u32> {
209 let mut err = nvim::Error::new();
210 let nr = unsafe { nvim_win_get_number(self.0, &mut err) };
211 choose!(err, Ok(nr.try_into().expect("always positive")))
212 }
213
214 pub fn get_position(&self) -> Result<(usize, usize)> {
220 let mut err = nvim::Error::new();
221 let arr =
222 unsafe { nvim_win_get_position(self.0, types::arena(), &mut err) };
223 choose!(err, {
224 let mut iter = arr.into_iter();
225 let line = usize::from_object(iter.next().unwrap())?;
226 let col = usize::from_object(iter.next().unwrap())?;
227 Ok((line, col))
228 })
229 }
230
231 pub fn get_tabpage(&self) -> Result<TabPage> {
237 let mut err = nvim::Error::new();
238 let handle = unsafe { nvim_win_get_tabpage(self.0, &mut err) };
239 choose!(err, Ok(handle.into()))
240 }
241
242 pub fn get_var<Var>(&self, name: &str) -> Result<Var>
248 where
249 Var: FromObject,
250 {
251 let mut err = nvim::Error::new();
252 let name = nvim::String::from(name);
253 let obj = unsafe {
254 nvim_win_get_var(
255 self.0,
256 name.as_nvim_str(),
257 types::arena(),
258 &mut err,
259 )
260 };
261 choose!(err, Ok(Var::from_object(obj)?))
262 }
263
264 pub fn get_width(&self) -> Result<u32> {
270 let mut err = nvim::Error::new();
271 let width = unsafe { nvim_win_get_width(self.0, &mut err) };
272 choose!(err, Ok(width.try_into().expect("always positive")))
273 }
274
275 pub fn hide(self) -> Result<()> {
281 let mut err = nvim::Error::new();
282 unsafe { nvim_win_hide(self.0, &mut err) };
283 choose!(err, ())
284 }
285
286 pub fn is_valid(&self) -> bool {
292 unsafe { nvim_win_is_valid(self.0) }
293 }
294
295 pub fn set_buf(&mut self, buffer: &Buffer) -> Result<()> {
301 let mut err = nvim::Error::new();
302 unsafe { nvim_win_set_buf(self.0, buffer.0, &mut err) };
303 choose!(err, ())
304 }
305
306 pub fn set_cursor(&mut self, line: usize, col: usize) -> Result<()> {
313 let mut err = nvim::Error::new();
314 let pos = Array::from_iter([line as Integer, col as Integer]);
315 unsafe { nvim_win_set_cursor(self.0, pos.non_owning(), &mut err) };
316 choose!(err, ())
317 }
318
319 pub fn set_height(&mut self, height: u32) -> Result<()> {
325 let mut err = nvim::Error::new();
326 unsafe { nvim_win_set_height(self.0, height.into(), &mut err) };
327 choose!(err, ())
328 }
329
330 pub fn set_hl_ns(&mut self, ns_id: u32) -> Result<()> {
340 let mut err = nvim::Error::new();
341 unsafe { nvim_win_set_hl_ns(self.0, ns_id.into(), &mut err) };
342 choose!(err, ())
343 }
344
345 pub fn set_var<Var>(&mut self, name: &str, value: Var) -> Result<()>
351 where
352 Var: ToObject,
353 {
354 let mut err = nvim::Error::new();
355 let name = nvim::String::from(name);
356 unsafe {
357 nvim_win_set_var(
358 self.0,
359 name.as_nvim_str(),
360 value.to_object()?.non_owning(),
361 &mut err,
362 )
363 };
364 choose!(err, ())
365 }
366
367 pub fn set_width(&mut self, width: u32) -> Result<()> {
373 let mut err = nvim::Error::new();
374 unsafe { nvim_win_set_width(self.0, width.into(), &mut err) };
375 choose!(err, ())
376 }
377
378 pub fn text_height(
385 &self,
386 opts: &WinTextHeightOpts,
387 ) -> Result<WinTextHeightInfos> {
388 let mut err = nvim::Error::new();
389 let dict = unsafe {
390 nvim_win_text_height(self.0, opts, types::arena(), &mut err)
391 };
392 choose!(err, dict.try_into().map_err(Into::into))
393 }
394}