1use std::fmt;
2use std::ops::RangeBounds;
3use std::path::{Path, PathBuf};
4use std::result::Result as StdResult;
5
6use luajit_bindings::{self as lua, Poppable, Pushable};
7use nvim_types::{
8 self as nvim,
9 conversion::{self, FromObject, ToObject},
10 Array,
11 BufHandle,
12 Dictionary,
13 Function,
14 Integer,
15 Object,
16};
17use serde::{Deserialize, Serialize};
18
19use crate::choose;
20use crate::ffi::buffer::*;
21use crate::iterator::SuperIterator;
22use crate::opts::*;
23use crate::types::{CommandArgs, CommandInfos, KeymapInfos, Mode};
24use crate::utils;
25use crate::StringOrFunction;
26use crate::LUA_INTERNAL_CALL;
27use crate::{Error, Result};
28
29#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
31pub struct Buffer(pub(crate) BufHandle);
32
33impl fmt::Debug for Buffer {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 f.debug_tuple("Buffer").field(&self.0).finish()
36 }
37}
38
39impl fmt::Display for Buffer {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 fmt::Debug::fmt(self, f)
42 }
43}
44
45impl<H: Into<BufHandle>> From<H> for Buffer {
46 #[inline(always)]
47 fn from(handle: H) -> Self {
48 Self(handle.into())
49 }
50}
51
52impl From<Buffer> for Object {
53 #[inline(always)]
54 fn from(buf: Buffer) -> Self {
55 buf.0.into()
56 }
57}
58
59impl From<&Buffer> for Object {
60 #[inline(always)]
61 fn from(buf: &Buffer) -> Self {
62 buf.0.into()
63 }
64}
65
66impl FromObject for Buffer {
67 #[inline(always)]
68 fn from_object(obj: Object) -> StdResult<Self, conversion::Error> {
69 Ok(BufHandle::from_object(obj)?.into())
70 }
71}
72
73impl Poppable for Buffer {
74 unsafe fn pop(
75 lstate: *mut lua::ffi::lua_State,
76 ) -> std::result::Result<Self, lua::Error> {
77 BufHandle::pop(lstate).map(Into::into)
78 }
79}
80
81impl Pushable for Buffer {
82 unsafe fn push(
83 self,
84 lstate: *mut lua::ffi::lua_State,
85 ) -> std::result::Result<std::ffi::c_int, lua::Error> {
86 self.0.push(lstate)
87 }
88}
89
90impl Buffer {
91 #[inline(always)]
93 pub fn current() -> Self {
94 crate::get_current_buf()
95 }
96
97 pub fn attach(
101 &self,
102 send_buffer: bool,
103 opts: &BufAttachOpts,
104 ) -> Result<()> {
105 let mut err = nvim::Error::new();
106 let opts = Dictionary::from(opts);
107 let has_attached = unsafe {
108 nvim_buf_attach(
109 LUA_INTERNAL_CALL,
110 self.0,
111 send_buffer,
112 opts.non_owning(),
113 &mut err,
114 )
115 };
116 choose!(
117 err,
118 match has_attached {
119 true => Ok(()),
120 _ => Err(Error::custom("Attaching to buffer failed")),
121 }
122 )
123 }
124
125 pub fn call<F, R>(&self, fun: F) -> Result<R>
129 where
130 F: FnOnce(()) -> Result<R> + 'static,
131 R: Pushable + FromObject,
132 {
133 let fun = Function::from_fn_once(fun);
134 let mut err = nvim::Error::new();
135 let obj = unsafe { nvim_buf_call(self.0, fun.lua_ref(), &mut err) };
136
137 choose!(err, {
138 fun.remove_from_lua_registry();
139 Ok(R::from_object(obj)?)
140 })
141 }
142
143 pub fn create_user_command<Cmd>(
147 &mut self,
148 name: &str,
149 command: Cmd,
150 opts: &CreateCommandOpts,
151 ) -> Result<()>
152 where
153 Cmd: StringOrFunction<CommandArgs, ()>,
154 {
155 let opts = KeyDict_user_command::from(opts);
156 let mut err = nvim::Error::new();
157 let name = nvim::String::from(name);
158 let command = command.to_object();
159 unsafe {
160 nvim_buf_create_user_command(
161 self.0,
162 name.non_owning(),
163 command.non_owning(),
164 &opts,
165 &mut err,
166 )
167 };
168 choose!(err, ())
169 }
170
171 pub fn del_keymap(&mut self, mode: Mode, lhs: &str) -> Result<()> {
175 let mut err = nvim::Error::new();
176 let mode = nvim::String::from(mode);
177 let lhs = nvim::String::from(lhs);
178 unsafe {
179 nvim_buf_del_keymap(
180 LUA_INTERNAL_CALL,
181 self.0,
182 mode.non_owning(),
183 lhs.non_owning(),
184 &mut err,
185 )
186 };
187 choose!(err, ())
188 }
189
190 pub fn del_mark(&mut self, name: char) -> Result<()> {
194 let mut err = nvim::Error::new();
195 let name = nvim::String::from(name);
196 let was_deleted =
197 unsafe { nvim_buf_del_mark(self.0, name.non_owning(), &mut err) };
198 choose!(
199 err,
200 match was_deleted {
201 true => Ok(()),
202 _ => Err(Error::custom("Couldn't delete mark")),
203 }
204 )
205 }
206
207 pub fn del_user_command(&mut self, name: &str) -> Result<()> {
215 let mut err = nvim::Error::new();
216 let name = nvim::String::from(name);
217 unsafe {
218 nvim_buf_del_user_command(self.0, name.non_owning(), &mut err)
219 };
220 choose!(err, ())
221 }
222
223 pub fn del_var(&mut self, name: &str) -> Result<()> {
229 let mut err = nvim::Error::new();
230 let name = nvim::String::from(name);
231 unsafe { nvim_buf_del_var(self.0, name.non_owning(), &mut err) };
232 choose!(err, ())
233 }
234
235 pub fn delete(self, opts: &BufDeleteOpts) -> Result<()> {
240 let mut err = nvim::Error::new();
241 let opts = Dictionary::from(opts);
242 unsafe { nvim_buf_delete(self.0, opts.non_owning(), &mut err) };
243 choose!(err, ())
244 }
245
246 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_commands(
255 &self,
256 opts: &GetCommandsOpts,
257 ) -> Result<impl SuperIterator<CommandInfos>> {
258 let mut err = nvim::Error::new();
259 let opts = KeyDict_get_commands::from(opts);
260 let cmds = unsafe { nvim_buf_get_commands(self.0, &opts, &mut err) };
261 choose!(
262 err,
263 Ok({
264 cmds.into_iter()
265 .map(|(_, cmd)| CommandInfos::from_object(cmd).unwrap())
266 })
267 )
268 }
269
270 pub fn get_keymap(
272 &self,
273 mode: Mode,
274 ) -> Result<impl SuperIterator<KeymapInfos>> {
275 let mut err = nvim::Error::new();
276 let mode = nvim::String::from(mode);
277 let maps = unsafe {
278 nvim_buf_get_keymap(
279 LUA_INTERNAL_CALL,
280 self.0,
281 mode.non_owning(),
282 &mut err,
283 )
284 };
285 choose!(
286 err,
287 Ok({
288 maps.into_iter()
289 .map(|obj| KeymapInfos::from_object(obj).unwrap())
290 })
291 )
292 }
293
294 pub fn get_lines<R>(
301 &self,
302 line_range: R,
303 strict_indexing: bool,
304 ) -> Result<impl SuperIterator<nvim::String>>
305 where
306 R: RangeBounds<usize>,
307 {
308 let mut err = nvim::Error::new();
309 let (start, end) = utils::range_to_limits(line_range);
310 let lines = unsafe {
311 nvim_buf_get_lines(
312 LUA_INTERNAL_CALL,
313 self.0,
314 start,
315 end,
316 strict_indexing,
317 &mut err,
318 )
319 };
320 choose!(
321 err,
322 Ok({
323 lines
324 .into_iter()
325 .map(|line| nvim::String::from_object(line).unwrap())
326 })
327 )
328 }
329
330 pub fn get_mark(&self, name: char) -> Result<(usize, usize)> {
335 let mut err = nvim::Error::new();
336 let name = nvim::String::from(name);
337 let mark =
338 unsafe { nvim_buf_get_mark(self.0, name.non_owning(), &mut err) };
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> {
351 let mut err = nvim::Error::new();
352 let name = unsafe { nvim_buf_get_name(self.0, &mut err) };
353 choose!(err, Ok(name.into()))
354 }
355
356 pub fn get_offset(&self, index: usize) -> Result<usize> {
360 let mut err = nvim::Error::new();
361 let offset =
362 unsafe { nvim_buf_get_offset(self.0, index as Integer, &mut err) };
363 choose!(err, Ok(offset.try_into().expect("offset is positive")))
364 }
365
366 pub fn get_option<Opt>(&self, name: &str) -> Result<Opt>
370 where
371 Opt: FromObject,
372 {
373 let mut err = nvim::Error::new();
374 let name = nvim::String::from(name);
375 let obj = unsafe {
376 nvim_buf_get_option(self.0, name.non_owning(), &mut err)
377 };
378 choose!(err, Ok(Opt::from_object(obj)?))
379 }
380
381 pub fn get_text<R>(
389 &self,
390 line_range: R,
391 start_col: usize,
392 end_col: usize,
393 opts: &GetTextOpts,
394 ) -> Result<impl SuperIterator<nvim::String>>
395 where
396 R: RangeBounds<usize>,
397 {
398 let mut err = nvim::Error::new();
399 let opts = Dictionary::from(opts);
400 let (start, end) = utils::range_to_limits(line_range);
401 let lines = unsafe {
402 nvim_buf_get_text(
403 LUA_INTERNAL_CALL,
404 self.0,
405 start,
406 start_col.try_into()?,
407 end,
408 end_col.try_into()?,
409 opts.non_owning(),
410 &mut err,
411 )
412 };
413 choose!(
414 err,
415 Ok({
416 lines
417 .into_iter()
418 .map(|line| nvim::String::from_object(line).unwrap())
419 })
420 )
421 }
422
423 pub fn get_var<Var>(&self, name: &str) -> Result<Var>
427 where
428 Var: FromObject,
429 {
430 let mut err = nvim::Error::new();
431 let name = nvim::String::from(name);
432 let obj =
433 unsafe { nvim_buf_get_var(self.0, name.non_owning(), &mut err) };
434 choose!(err, Ok(Var::from_object(obj)?))
435 }
436
437 pub fn is_loaded(&self) -> bool {
441 unsafe { nvim_buf_is_loaded(self.0) }
442 }
443
444 pub fn is_valid(&self) -> bool {
448 unsafe { nvim_buf_is_valid(self.0) }
449 }
450
451 pub fn line_count(&self) -> Result<usize> {
455 let mut err = nvim::Error::new();
456 let count = unsafe { nvim_buf_line_count(self.0, &mut err) };
457 choose!(err, Ok(count.try_into().expect("always positive")))
458 }
459
460 pub fn set_keymap(
467 &mut self,
468 mode: Mode,
469 lhs: &str,
470 rhs: &str,
471 opts: &SetKeymapOpts,
472 ) -> Result<()> {
473 let mode = nvim::String::from(mode);
474 let lhs = nvim::String::from(lhs);
475 let rhs = nvim::String::from(rhs);
476 let opts = KeyDict_keymap::from(opts);
477 let mut err = nvim::Error::new();
478 unsafe {
479 nvim_buf_set_keymap(
480 LUA_INTERNAL_CALL,
481 self.0,
482 mode.non_owning(),
483 lhs.non_owning(),
484 rhs.non_owning(),
485 &opts,
486 &mut err,
487 )
488 };
489 choose!(err, ())
490 }
491
492 pub fn set_lines<Line, Lines, R>(
499 &mut self,
500 line_range: R,
501 strict_indexing: bool,
502 replacement: Lines,
503 ) -> Result<()>
504 where
505 R: RangeBounds<usize>,
506 Lines: IntoIterator<Item = Line>,
507 Line: Into<nvim::String>,
508 {
509 let rpl = replacement.into_iter().map(Into::into).collect::<Array>();
510 let mut err = nvim::Error::new();
511 let (start, end) = utils::range_to_limits(line_range);
512 unsafe {
513 nvim_buf_set_lines(
514 LUA_INTERNAL_CALL,
515 self.0,
516 start,
517 end,
518 strict_indexing,
519 rpl.non_owning(),
520 &mut err,
521 )
522 };
523 choose!(err, ())
524 }
525
526 pub fn set_mark(
531 &mut self,
532 name: char,
533 line: usize,
534 col: usize,
535 ) -> Result<()> {
536 let mut err = nvim::Error::new();
537 let name = nvim::String::from(name);
538 let mark_was_set = unsafe {
539 nvim_buf_set_mark(
540 self.0,
541 name.non_owning(),
542 line.try_into()?,
543 col.try_into()?,
544 Dictionary::new().non_owning(),
545 &mut err,
546 )
547 };
548 choose!(
549 err,
550 match mark_was_set {
551 true => Ok(()),
552 _ => Err(Error::custom("Couldn't set mark")),
553 }
554 )
555 }
556
557 pub fn set_name<Name: AsRef<Path>>(&mut self, name: Name) -> Result<()> {
561 let name = nvim::String::from(name.as_ref().to_owned());
562 let mut err = nvim::Error::new();
563 unsafe { nvim_buf_set_name(self.0, name.non_owning(), &mut err) };
564 choose!(err, ())
565 }
566
567 pub fn set_option<V>(&mut self, name: &str, value: V) -> Result<()>
572 where
573 V: ToObject,
574 {
575 let mut err = nvim::Error::new();
576 let name = nvim::String::from(name);
577 unsafe {
578 nvim_buf_set_option(
579 LUA_INTERNAL_CALL,
580 self.0,
581 name.non_owning(),
582 value.to_object()?.non_owning(),
583 &mut err,
584 )
585 };
586 choose!(err, ())
587 }
588
589 pub fn set_text<Line, Lines, R>(
594 &mut self,
595 line_range: R,
596 start_col: usize,
597 end_col: usize,
598 replacement: Lines,
599 ) -> Result<()>
600 where
601 R: RangeBounds<usize>,
602 Lines: IntoIterator<Item = Line>,
603 Line: Into<nvim::String>,
604 {
605 let mut err = nvim::Error::new();
606 let (start, end) = utils::range_to_limits(line_range);
607 unsafe {
608 nvim_buf_set_text(
609 LUA_INTERNAL_CALL,
610 self.0,
611 start,
612 start_col.try_into()?,
613 end,
614 end_col.try_into()?,
615 replacement
616 .into_iter()
617 .map(|line| line.into())
618 .collect::<Array>()
619 .non_owning(),
620 &mut err,
621 )
622 };
623 choose!(err, ())
624 }
625
626 pub fn set_var<V>(&mut self, name: &str, value: V) -> Result<()>
632 where
633 V: ToObject,
634 {
635 let mut err = nvim::Error::new();
636 let name = nvim::String::from(name);
637 unsafe {
638 nvim_buf_set_var(
639 self.0,
640 name.non_owning(),
641 value.to_object()?.non_owning(),
642 &mut err,
643 )
644 };
645 choose!(err, ())
646 }
647}