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 Array,
12 BufHandle,
13 Function,
14 Integer,
15 Object,
16 conversion::{self, FromObject, ToObject},
17};
18
19use crate::LUA_INTERNAL_CALL;
20use crate::SuperIterator;
21use crate::choose;
22use crate::ffi::buffer::*;
23use crate::opts::*;
24use crate::types::{KeymapInfos, Mode};
25use crate::utils;
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::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::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 let has_attached = unsafe {
115 nvim_buf_attach(
116 LUA_INTERNAL_CALL,
117 self.0,
118 send_buffer,
119 opts,
120 &mut err,
121 )
122 };
123
124 choose!(
125 err,
126 match has_attached {
127 true => Ok(()),
128 _ => Err(Error::custom("Attaching to buffer failed")),
129 }
130 )
131 }
132
133 pub fn call<F, Res, Ret>(&self, fun: F) -> Result<Ret>
139 where
140 F: FnOnce(()) -> Res + 'static,
141 Res: IntoResult<Ret>,
142 Res::Error: StdError + 'static,
143 Ret: Pushable + FromObject,
144 {
145 let fun = Function::from_fn_once(fun);
146 let mut err = nvim::Error::new();
147
148 let ref_or_nil =
149 unsafe { nvim_buf_call(self.0, fun.lua_ref(), &mut err) };
150
151 let lua_ref = match ref_or_nil.kind() {
152 types::ObjectKind::LuaRef => unsafe {
153 ref_or_nil.as_luaref_unchecked()
154 },
155 types::ObjectKind::Nil => {
156 return Ret::from_object(Object::nil()).map_err(Into::into);
157 },
158 other => panic!("Unexpected object kind: {other:?}"),
159 };
160
161 let obj = unsafe {
162 lua::with_state(|lstate| {
163 lua::ffi::lua_rawgeti(
164 lstate,
165 lua::ffi::LUA_REGISTRYINDEX,
166 lua_ref,
167 );
168 Object::pop(lstate)
169 })
170 }
171 .map_err(Error::custom)?;
172
173 choose!(err, {
174 fun.remove_from_lua_registry();
175 Ok(Ret::from_object(obj)?)
176 })
177 }
178
179 pub fn del_keymap(&mut self, mode: Mode, lhs: &str) -> Result<()> {
185 let mut err = nvim::Error::new();
186 let mode = nvim::String::from(mode);
187 let lhs = nvim::String::from(lhs);
188 unsafe {
189 nvim_buf_del_keymap(
190 LUA_INTERNAL_CALL,
191 self.0,
192 mode.as_nvim_str(),
193 lhs.as_nvim_str(),
194 &mut err,
195 )
196 };
197 choose!(err, ())
198 }
199
200 pub fn del_mark(&mut self, name: char) -> Result<()> {
206 let mut err = nvim::Error::new();
207 let name = nvim::String::from(name);
208 let was_deleted =
209 unsafe { nvim_buf_del_mark(self.0, name.as_nvim_str(), &mut err) };
210 choose!(
211 err,
212 match was_deleted {
213 true => Ok(()),
214
215 _ => Err(Error::custom("Couldn't delete mark")),
216 }
217 )
218 }
219
220 pub fn del_var(&mut self, name: &str) -> Result<()> {
226 let mut err = nvim::Error::new();
227 let name = nvim::String::from(name);
228 unsafe { nvim_buf_del_var(self.0, name.as_nvim_str(), &mut err) };
229 choose!(err, ())
230 }
231
232 pub fn delete(self, opts: &BufDeleteOpts) -> Result<()> {
239 let mut err = nvim::Error::new();
240 unsafe { nvim_buf_delete(self.0, opts, &mut err) };
241 choose!(err, ())
242 }
243
244 pub fn get_changedtick(&self) -> Result<u32> {
248 let mut err = nvim::Error::new();
249 let ct = unsafe { nvim_buf_get_changedtick(self.0, &mut err) };
250 choose!(err, Ok(ct.try_into().expect("always positive")))
251 }
252
253 pub fn get_keymap(
257 &self,
258 mode: Mode,
259 ) -> Result<impl SuperIterator<KeymapInfos>> {
260 let mut err = nvim::Error::new();
261 let mode = nvim::String::from(mode);
262 let maps = unsafe {
263 nvim_buf_get_keymap(
264 self.0,
265 mode.as_nvim_str(),
266 types::arena(),
267 &mut err,
268 )
269 };
270 choose!(
271 err,
272 Ok({
273 maps.into_iter()
274 .map(|obj| KeymapInfos::from_object(obj).unwrap())
275 })
276 )
277 }
278
279 pub fn get_lines<R>(
286 &self,
287 line_range: R,
288 strict_indexing: bool,
289 ) -> Result<impl SuperIterator<nvim::String>>
290 where
291 R: RangeBounds<usize>,
292 {
293 let mut err = nvim::Error::new();
294 let (start, end) = utils::range_to_limits(line_range);
295 let lines = unsafe {
296 nvim_buf_get_lines(
297 LUA_INTERNAL_CALL,
298 self.0,
299 start,
300 end,
301 strict_indexing,
302 types::arena(),
303 core::ptr::null_mut(),
306 &mut err,
307 )
308 };
309 choose!(
310 err,
311 Ok({
312 lines
313 .into_iter()
314 .map(|line| nvim::String::from_object(line).unwrap())
315 })
316 )
317 }
318
319 pub fn get_mark(&self, name: char) -> Result<(usize, usize)> {
326 let mut err = nvim::Error::new();
327 let name = nvim::String::from(name);
328 let mark = unsafe {
329 nvim_buf_get_mark(
330 self.0,
331 name.as_nvim_str(),
332 types::arena(),
333 &mut err,
334 )
335 };
336 choose!(err, {
337 let mut iter = mark.into_iter().map(usize::from_object);
338 let row = iter.next().expect("row is present")?;
339 let col = iter.next().expect("col is present")?;
340 Ok((row, col))
341 })
342 }
343
344 pub fn get_name(&self) -> Result<PathBuf> {
350 let mut err = nvim::Error::new();
351 let name =
352 unsafe { nvim_buf_get_name(self.0, types::arena(), &mut err) };
353 choose!(err, Ok(name.into()))
354 }
355
356 pub fn get_offset(&self, index: usize) -> Result<usize> {
362 let mut err = nvim::Error::new();
363 let offset =
364 unsafe { nvim_buf_get_offset(self.0, index as Integer, &mut err) };
365 choose!(err, Ok(offset.try_into().expect("offset is positive")))
366 }
367
368 pub fn get_text<R>(
378 &self,
379 line_range: R,
380 start_col: usize,
381 end_col: usize,
382 opts: &GetTextOpts,
383 ) -> Result<impl SuperIterator<nvim::String>>
384 where
385 R: RangeBounds<usize>,
386 {
387 let mut err = nvim::Error::new();
388 let (start, end) = utils::range_to_limits(line_range);
389 let lines = unsafe {
390 nvim_buf_get_text(
391 LUA_INTERNAL_CALL,
392 self.0,
393 start,
394 start_col.try_into()?,
395 end,
396 end_col.try_into()?,
397 opts,
398 types::arena(),
399 std::ptr::null_mut(),
402 &mut err,
403 )
404 };
405 choose!(
406 err,
407 Ok({
408 lines
409 .into_iter()
410 .map(|line| nvim::String::from_object(line).unwrap())
411 })
412 )
413 }
414
415 pub fn get_var<Var>(&self, name: &str) -> Result<Var>
421 where
422 Var: FromObject,
423 {
424 let mut err = nvim::Error::new();
425 let name = nvim::String::from(name);
426 let obj = unsafe {
427 nvim_buf_get_var(
428 self.0,
429 name.as_nvim_str(),
430 types::arena(),
431 &mut err,
432 )
433 };
434 choose!(err, Ok(Var::from_object(obj)?))
435 }
436
437 pub fn is_loaded(&self) -> bool {
443 unsafe { nvim_buf_is_loaded(self.0) }
444 }
445
446 pub fn is_valid(&self) -> bool {
452 unsafe { nvim_buf_is_valid(self.0) }
453 }
454
455 pub fn line_count(&self) -> Result<usize> {
461 let mut err = nvim::Error::new();
462 let count = unsafe { nvim_buf_line_count(self.0, &mut err) };
463 choose!(err, Ok(count.try_into().expect("always positive")))
464 }
465
466 pub fn set_keymap(
473 &mut self,
474 mode: Mode,
475 lhs: &str,
476 rhs: &str,
477 opts: &SetKeymapOpts,
478 ) -> Result<()> {
479 let mode = nvim::String::from(mode);
480 let lhs = nvim::String::from(lhs);
481 let rhs = nvim::String::from(rhs);
482 let mut err = nvim::Error::new();
483 unsafe {
484 nvim_buf_set_keymap(
485 LUA_INTERNAL_CALL,
486 self.0,
487 mode.as_nvim_str(),
488 lhs.as_nvim_str(),
489 rhs.as_nvim_str(),
490 opts,
491 &mut err,
492 )
493 };
494 choose!(err, ())
495 }
496
497 pub fn set_lines<Line, Lines, R>(
504 &mut self,
505 line_range: R,
506 strict_indexing: bool,
507 replacement: Lines,
508 ) -> Result<()>
509 where
510 R: RangeBounds<usize>,
511 Lines: IntoIterator<Item = Line>,
512 Line: Into<nvim::String>,
513 {
514 let rpl = replacement.into_iter().map(Into::into).collect::<Array>();
515 let mut err = nvim::Error::new();
516 let (start, end) = utils::range_to_limits(line_range);
517 unsafe {
518 nvim_buf_set_lines(
519 LUA_INTERNAL_CALL,
520 self.0,
521 start,
522 end,
523 strict_indexing,
524 rpl.non_owning(),
525 types::arena(),
526 &mut err,
527 )
528 };
529 choose!(err, ())
530 }
531
532 pub fn set_mark(
539 &mut self,
540 name: char,
541 line: usize,
542 col: usize,
543 opts: &SetMarkOpts,
544 ) -> Result<()> {
545 let mut err = nvim::Error::new();
546 let name = nvim::String::from(name);
547 let mark_was_set = unsafe {
548 nvim_buf_set_mark(
549 self.0,
550 name.as_nvim_str(),
551 line.try_into()?,
552 col.try_into()?,
553 opts,
554 &mut err,
555 )
556 };
557 choose!(
558 err,
559 match mark_was_set {
560 true => Ok(()),
561 _ => Err(Error::custom("Couldn't set mark")),
562 }
563 )
564 }
565
566 pub fn set_name<Name: AsRef<Path>>(&mut self, name: Name) -> Result<()> {
572 let name = nvim::String::from(name.as_ref());
573 let mut err = nvim::Error::new();
574 unsafe { nvim_buf_set_name(self.0, name.as_nvim_str(), &mut err) };
575 choose!(err, ())
576 }
577
578 pub fn set_text<Line, Lines, R>(
585 &mut self,
586 line_range: R,
587 start_col: usize,
588 end_col: usize,
589 replacement: Lines,
590 ) -> Result<()>
591 where
592 R: RangeBounds<usize>,
593 Lines: IntoIterator<Item = Line>,
594 Line: Into<nvim::String>,
595 {
596 let mut err = nvim::Error::new();
597 let (start, end) = utils::range_to_limits(line_range);
598 unsafe {
599 nvim_buf_set_text(
600 LUA_INTERNAL_CALL,
601 self.0,
602 start,
603 start_col.try_into()?,
604 end,
605 end_col.try_into()?,
606 replacement
607 .into_iter()
608 .map(|line| line.into())
609 .collect::<Array>()
610 .non_owning(),
611 types::arena(),
612 &mut err,
613 )
614 };
615 choose!(err, ())
616 }
617
618 pub fn set_var<V>(&mut self, name: &str, value: V) -> Result<()>
624 where
625 V: ToObject,
626 {
627 let mut err = nvim::Error::new();
628 let name = nvim::String::from(name);
629 unsafe {
630 nvim_buf_set_var(
631 self.0,
632 name.as_nvim_str(),
633 value.to_object()?.non_owning(),
634 &mut err,
635 )
636 };
637 choose!(err, ())
638 }
639}