1use core::ops::RangeBounds;
2use std::error::Error as StdError;
3use std::fmt;
4use std::path::{Path, PathBuf};
5use std::result::Result as StdResult;
6
7use luajit::{self as lua, Poppable, Pushable};
8use serde::{Deserialize, Serialize};
9use types::{
10 self as nvim,
11 conversion::{self, FromObject, ToObject},
12 Array,
13 BufHandle,
14 Function,
15 Integer,
16 Object,
17};
18
19use crate::choose;
20use crate::ffi::buffer::*;
21use crate::opts::*;
22use crate::types::{KeymapInfos, Mode};
23use crate::utils;
24use crate::SuperIterator;
25use crate::LUA_INTERNAL_CALL;
26use crate::{Error, IntoResult, Result};
27
28#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
30pub struct Buffer(pub(crate) BufHandle);
31
32impl fmt::Debug for Buffer {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 f.debug_tuple("Buffer").field(&self.0).finish()
35 }
36}
37
38impl fmt::Display for Buffer {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 fmt::Debug::fmt(self, f)
41 }
42}
43
44impl<H: Into<BufHandle>> From<H> for Buffer {
45 #[inline(always)]
46 fn from(handle: H) -> Self {
47 Self(handle.into())
48 }
49}
50
51impl From<Buffer> for Object {
52 #[inline(always)]
53 fn from(buf: Buffer) -> Self {
54 buf.0.into()
55 }
56}
57
58impl From<&Buffer> for Object {
59 #[inline(always)]
60 fn from(buf: &Buffer) -> Self {
61 buf.0.into()
62 }
63}
64
65impl FromObject for Buffer {
66 #[inline(always)]
67 fn from_object(obj: Object) -> StdResult<Self, conversion::Error> {
68 Ok(BufHandle::from_object(obj)?.into())
69 }
70}
71
72impl Poppable for Buffer {
73 unsafe fn pop(
74 lstate: *mut lua::ffi::lua_State,
75 ) -> std::result::Result<Self, lua::Error> {
76 BufHandle::pop(lstate).map(Into::into)
77 }
78}
79
80impl Pushable for Buffer {
81 unsafe fn push(
82 self,
83 lstate: *mut lua::ffi::lua_State,
84 ) -> std::result::Result<std::ffi::c_int, lua::Error> {
85 self.0.push(lstate)
86 }
87}
88
89impl Buffer {
90 #[inline(always)]
92 pub fn current() -> Self {
93 crate::get_current_buf()
94 }
95
96 #[inline(always)]
98 pub fn handle(&self) -> i32 {
99 self.0
100 }
101
102 pub fn attach(
108 &self,
109 send_buffer: bool,
110 opts: &BufAttachOpts,
111 ) -> Result<()> {
112 let mut err = nvim::Error::new();
113
114 #[cfg(not(feature = "neovim-0-10"))] let opts = types::Dictionary::from(opts);
116
117 let has_attached = unsafe {
118 nvim_buf_attach(
119 LUA_INTERNAL_CALL,
120 self.0,
121 send_buffer,
122 #[cfg(not(feature = "neovim-0-10"))] opts.non_owning(),
124 #[cfg(feature = "neovim-0-10")] opts,
126 &mut err,
127 )
128 };
129
130 choose!(
131 err,
132 match has_attached {
133 true => Ok(()),
134 _ => Err(Error::custom("Attaching to buffer failed")),
135 }
136 )
137 }
138
139 pub fn call<F, Res, Ret>(&self, fun: F) -> Result<Ret>
145 where
146 F: FnOnce(()) -> Res + 'static,
147 Res: IntoResult<Ret>,
148 Res::Error: StdError + 'static,
149 Ret: Pushable + FromObject,
150 {
151 let fun = Function::from_fn_once(fun);
152 let mut err = nvim::Error::new();
153 let obj = unsafe { nvim_buf_call(self.0, fun.lua_ref(), &mut err) };
154
155 choose!(err, {
156 fun.remove_from_lua_registry();
157 Ok(Ret::from_object(obj)?)
158 })
159 }
160
161 pub fn del_keymap(&mut self, mode: Mode, lhs: &str) -> Result<()> {
167 let mut err = nvim::Error::new();
168 let mode = nvim::String::from(mode);
169 let lhs = nvim::String::from(lhs);
170 unsafe {
171 nvim_buf_del_keymap(
172 LUA_INTERNAL_CALL,
173 self.0,
174 mode.non_owning(),
175 lhs.non_owning(),
176 &mut err,
177 )
178 };
179 choose!(err, ())
180 }
181
182 pub fn del_mark(&mut self, name: char) -> Result<()> {
188 let mut err = nvim::Error::new();
189 let name = nvim::String::from(name);
190 let was_deleted =
191 unsafe { nvim_buf_del_mark(self.0, name.non_owning(), &mut err) };
192 choose!(
193 err,
194 match was_deleted {
195 true => Ok(()),
196
197 _ => Err(Error::custom("Couldn't delete mark")),
198 }
199 )
200 }
201
202 pub fn del_var(&mut self, name: &str) -> Result<()> {
208 let mut err = nvim::Error::new();
209 let name = nvim::String::from(name);
210 unsafe { nvim_buf_del_var(self.0, name.non_owning(), &mut err) };
211 choose!(err, ())
212 }
213
214 pub fn delete(self, opts: &BufDeleteOpts) -> Result<()> {
221 let mut err = nvim::Error::new();
222
223 #[cfg(not(feature = "neovim-0-10"))] let opts = types::Dictionary::from(opts);
225
226 unsafe {
227 nvim_buf_delete(
228 self.0,
229 #[cfg(not(feature = "neovim-0-10"))] opts.non_owning(),
231 #[cfg(feature = "neovim-0-10")] opts,
233 &mut err,
234 )
235 };
236
237 choose!(err, ())
238 }
239
240 pub fn get_changedtick(&self) -> Result<u32> {
244 let mut err = nvim::Error::new();
245 let ct = unsafe { nvim_buf_get_changedtick(self.0, &mut err) };
246 choose!(err, Ok(ct.try_into().expect("always positive")))
247 }
248
249 pub fn get_keymap(
253 &self,
254 mode: Mode,
255 ) -> Result<impl SuperIterator<KeymapInfos>> {
256 let mut err = nvim::Error::new();
257 let mode = nvim::String::from(mode);
258 let maps = unsafe {
259 nvim_buf_get_keymap(
260 self.0,
261 mode.non_owning(),
262 #[cfg(feature = "neovim-0-10")] types::arena(),
264 &mut err,
265 )
266 };
267 choose!(
268 err,
269 Ok({
270 maps.into_iter()
271 .map(|obj| KeymapInfos::from_object(obj).unwrap())
272 })
273 )
274 }
275
276 pub fn get_lines<R>(
283 &self,
284 line_range: R,
285 strict_indexing: bool,
286 ) -> Result<impl SuperIterator<nvim::String>>
287 where
288 R: RangeBounds<usize>,
289 {
290 let mut err = nvim::Error::new();
291 let (start, end) = utils::range_to_limits(line_range);
292 let lines = unsafe {
293 nvim_buf_get_lines(
294 LUA_INTERNAL_CALL,
295 self.0,
296 start,
297 end,
298 strict_indexing,
299 #[cfg(feature = "neovim-0-10")] types::arena(),
301 #[cfg(any(
302 feature = "neovim-0-9",
303 feature = "neovim-nightly"
304 ))]
305 core::ptr::null_mut(),
308 &mut err,
309 )
310 };
311 choose!(
312 err,
313 Ok({
314 lines
315 .into_iter()
316 .map(|line| nvim::String::from_object(line).unwrap())
317 })
318 )
319 }
320
321 pub fn get_mark(&self, name: char) -> Result<(usize, usize)> {
328 let mut err = nvim::Error::new();
329 let name = nvim::String::from(name);
330 let mark = unsafe {
331 nvim_buf_get_mark(
332 self.0,
333 name.non_owning(),
334 #[cfg(feature = "neovim-0-10")] types::arena(),
336 &mut err,
337 )
338 };
339 choose!(err, {
340 let mut iter = mark.into_iter().map(usize::from_object);
341 let row = iter.next().expect("row is present")?;
342 let col = iter.next().expect("col is present")?;
343 Ok((row, col))
344 })
345 }
346
347 pub fn get_name(&self) -> Result<PathBuf> {
353 let mut err = nvim::Error::new();
354 let name =
355 unsafe { nvim_buf_get_name(self.0, types::arena(), &mut err) };
356 choose!(err, Ok(name.into()))
357 }
358
359 pub fn get_offset(&self, index: usize) -> Result<usize> {
365 let mut err = nvim::Error::new();
366 let offset =
367 unsafe { nvim_buf_get_offset(self.0, index as Integer, &mut err) };
368 choose!(err, Ok(offset.try_into().expect("offset is positive")))
369 }
370
371 pub fn get_text<R>(
381 &self,
382 line_range: R,
383 start_col: usize,
384 end_col: usize,
385 opts: &GetTextOpts,
386 ) -> Result<impl SuperIterator<nvim::String>>
387 where
388 R: RangeBounds<usize>,
389 {
390 let mut err = nvim::Error::new();
391 #[cfg(not(feature = "neovim-0-10"))] let opts = types::Dictionary::from(opts);
393 let (start, end) = utils::range_to_limits(line_range);
394 let lines = unsafe {
395 nvim_buf_get_text(
396 LUA_INTERNAL_CALL,
397 self.0,
398 start,
399 start_col.try_into()?,
400 end,
401 end_col.try_into()?,
402 #[cfg(not(feature = "neovim-0-10"))] opts.non_owning(),
404 #[cfg(feature = "neovim-0-10")] opts,
406 #[cfg(feature = "neovim-0-10")] types::arena(),
408 #[cfg(any(
409 feature = "neovim-0-9",
410 feature = "neovim-nightly"
411 ))]
412 std::ptr::null_mut(),
414 &mut err,
415 )
416 };
417 choose!(
418 err,
419 Ok({
420 lines
421 .into_iter()
422 .map(|line| nvim::String::from_object(line).unwrap())
423 })
424 )
425 }
426
427 pub fn get_var<Var>(&self, name: &str) -> Result<Var>
433 where
434 Var: FromObject,
435 {
436 let mut err = nvim::Error::new();
437 let name = nvim::String::from(name);
438 let obj = unsafe {
439 nvim_buf_get_var(
440 self.0,
441 name.non_owning(),
442 #[cfg(feature = "neovim-0-10")] types::arena(),
444 &mut err,
445 )
446 };
447 choose!(err, Ok(Var::from_object(obj)?))
448 }
449
450 pub fn is_loaded(&self) -> bool {
456 unsafe { nvim_buf_is_loaded(self.0) }
457 }
458
459 pub fn is_valid(&self) -> bool {
465 unsafe { nvim_buf_is_valid(self.0) }
466 }
467
468 pub fn line_count(&self) -> Result<usize> {
474 let mut err = nvim::Error::new();
475 let count = unsafe { nvim_buf_line_count(self.0, &mut err) };
476 choose!(err, Ok(count.try_into().expect("always positive")))
477 }
478
479 pub fn set_keymap(
486 &mut self,
487 mode: Mode,
488 lhs: &str,
489 rhs: &str,
490 opts: &SetKeymapOpts,
491 ) -> Result<()> {
492 let mode = nvim::String::from(mode);
493 let lhs = nvim::String::from(lhs);
494 let rhs = nvim::String::from(rhs);
495 let mut err = nvim::Error::new();
496 unsafe {
497 nvim_buf_set_keymap(
498 LUA_INTERNAL_CALL,
499 self.0,
500 mode.non_owning(),
501 lhs.non_owning(),
502 rhs.non_owning(),
503 opts,
504 &mut err,
505 )
506 };
507 choose!(err, ())
508 }
509
510 pub fn set_lines<Line, Lines, R>(
517 &mut self,
518 line_range: R,
519 strict_indexing: bool,
520 replacement: Lines,
521 ) -> Result<()>
522 where
523 R: RangeBounds<usize>,
524 Lines: IntoIterator<Item = Line>,
525 Line: Into<nvim::String>,
526 {
527 let rpl = replacement.into_iter().map(Into::into).collect::<Array>();
528 let mut err = nvim::Error::new();
529 let (start, end) = utils::range_to_limits(line_range);
530 unsafe {
531 nvim_buf_set_lines(
532 LUA_INTERNAL_CALL,
533 self.0,
534 start,
535 end,
536 strict_indexing,
537 rpl.non_owning(),
538 #[cfg(feature = "neovim-0-10")] types::arena(),
540 &mut err,
541 )
542 };
543 choose!(err, ())
544 }
545
546 pub fn set_mark(
553 &mut self,
554 name: char,
555 line: usize,
556 col: usize,
557 opts: &SetMarkOpts,
558 ) -> Result<()> {
559 let mut err = nvim::Error::new();
560 let name = nvim::String::from(name);
561 #[cfg(not(feature = "neovim-0-10"))] let opts = types::Dictionary::from(opts);
563 let mark_was_set = unsafe {
564 nvim_buf_set_mark(
565 self.0,
566 name.non_owning(),
567 line.try_into()?,
568 col.try_into()?,
569 #[cfg(not(feature = "neovim-0-10"))] opts.non_owning(),
571 #[cfg(feature = "neovim-0-10")] opts,
573 &mut err,
574 )
575 };
576 choose!(
577 err,
578 match mark_was_set {
579 true => Ok(()),
580 _ => Err(Error::custom("Couldn't set mark")),
581 }
582 )
583 }
584
585 pub fn set_name<Name: AsRef<Path>>(&mut self, name: Name) -> Result<()> {
591 let name = nvim::String::from(name.as_ref());
592 let mut err = nvim::Error::new();
593 unsafe { nvim_buf_set_name(self.0, name.non_owning(), &mut err) };
594 choose!(err, ())
595 }
596
597 pub fn set_text<Line, Lines, R>(
604 &mut self,
605 line_range: R,
606 start_col: usize,
607 end_col: usize,
608 replacement: Lines,
609 ) -> Result<()>
610 where
611 R: RangeBounds<usize>,
612 Lines: IntoIterator<Item = Line>,
613 Line: Into<nvim::String>,
614 {
615 let mut err = nvim::Error::new();
616 let (start, end) = utils::range_to_limits(line_range);
617 unsafe {
618 nvim_buf_set_text(
619 LUA_INTERNAL_CALL,
620 self.0,
621 start,
622 start_col.try_into()?,
623 end,
624 end_col.try_into()?,
625 replacement
626 .into_iter()
627 .map(|line| line.into())
628 .collect::<Array>()
629 .non_owning(),
630 #[cfg(feature = "neovim-0-10")] types::arena(),
632 &mut err,
633 )
634 };
635 choose!(err, ())
636 }
637
638 pub fn set_var<V>(&mut self, name: &str, value: V) -> Result<()>
644 where
645 V: ToObject,
646 {
647 let mut err = nvim::Error::new();
648 let name = nvim::String::from(name);
649 unsafe {
650 nvim_buf_set_var(
651 self.0,
652 name.non_owning(),
653 value.to_object()?.non_owning(),
654 &mut err,
655 )
656 };
657 choose!(err, ())
658 }
659}