rune/core/object/
buffer.rsuse super::{Gc, Object, ObjectType, TagType, WithLifetime};
use crate::{
core::{
error::{Type, TypeError},
gc::{Block, Context, GcHeap, GcState, Trace},
},
NewtypeMarkable,
};
use anyhow::{bail, Result};
use macro_attr_2018::macro_attr;
use newtype_derive_2018::*;
use rune_macros::Trace;
use std::{
fmt::Display,
ops::{Deref, DerefMut},
sync::{Mutex, MutexGuard},
};
use text_buffer::Buffer as TextBuffer;
#[derive(Debug)]
pub(crate) struct OpenBuffer<'a> {
data: MutexGuard<'a, Option<BufferData>>,
back_ref: &'a LispBuffer,
}
impl OpenBuffer<'_> {
fn get(&self) -> &BufferData {
self.data.as_ref().unwrap()
}
fn get_mut(&mut self) -> &mut BufferData {
self.data.as_mut().unwrap()
}
pub(crate) fn kill(&mut self) -> bool {
let killed = self.data.is_some();
*self.data = None;
killed
}
pub(crate) fn lisp_buffer<'ob>(&self, cx: &'ob Context) -> &'ob LispBuffer {
cx.bind(self.back_ref)
}
pub(crate) fn insert(&mut self, arg: Object) -> Result<()> {
match arg.untag() {
ObjectType::Int(i) => {
let Ok(u_32) = i.try_into() else { bail!("{i} is an invalid char") };
let Some(chr) = char::from_u32(u_32) else { bail!("{i} is an Invalid char") };
self.get_mut().text.insert_char(chr);
}
ObjectType::String(s) => self.get_mut().text.insert(s),
x => bail!(TypeError::new(Type::String, x)),
}
Ok(())
}
pub(crate) fn slice_with_gap(&self, beg: usize, end: usize) -> Result<(&str, &str)> {
let beg = self.in_range(beg)?;
let end = self.in_range(end)?;
Ok(self.get().text.slice(beg..end))
}
pub(crate) fn delete(&mut self, beg: usize, end: usize) -> Result<()> {
let beg = self.in_range(beg)?;
let end = self.in_range(end)?;
self.get_mut().text.delete_range(beg, end);
Ok(())
}
fn in_range(&self, pos: usize) -> Result<usize> {
if pos == 0 || pos > self.get().text.len_chars() + 1 {
bail!("Position {pos} out of range in {}", self.get().name);
}
Ok(pos - 1)
}
}
impl<'new> WithLifetime<'new> for OpenBuffer<'_> {
type Out = OpenBuffer<'new>;
unsafe fn with_lifetime(self) -> Self::Out {
std::mem::transmute(self)
}
}
impl PartialEq<str> for OpenBuffer<'_> {
fn eq(&self, other: &str) -> bool {
self.get().text == other
}
}
impl Deref for OpenBuffer<'_> {
type Target = BufferData;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl DerefMut for OpenBuffer<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
#[derive(Debug)]
pub(crate) struct BufferData {
pub(crate) name: String,
pub(crate) text: TextBuffer,
}
#[derive(Debug)]
pub(crate) struct LispBufferInner {
text_buffer: Mutex<Option<BufferData>>,
}
macro_attr! {
#[derive(PartialEq, Eq, Trace, NewtypeDebug!, NewtypeDisplay!, NewtypeDeref!, NewtypeMarkable!)]
pub(crate) struct LispBuffer(GcHeap<LispBufferInner>);
}
impl LispBuffer {
pub(crate) fn create(name: String, block: &Block<true>) -> &LispBuffer {
let buffer = unsafe { Self::new(name, block) };
block.objects.alloc(buffer)
}
pub(crate) unsafe fn new(name: String, _: &Block<true>) -> LispBuffer {
let new = LispBufferInner {
text_buffer: Mutex::new(Some(BufferData { name, text: TextBuffer::new() })),
};
Self(GcHeap::new(new, true))
}
pub(in crate::core) fn lock(&self) -> Result<OpenBuffer<'_>> {
let guard = self.text_buffer.lock().unwrap();
if guard.is_none() {
bail!("selecting deleted buffer");
}
Ok(OpenBuffer { data: guard, back_ref: self })
}
}
impl PartialEq for LispBufferInner {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl PartialEq<OpenBuffer<'_>> for LispBuffer {
fn eq(&self, other: &OpenBuffer) -> bool {
other.back_ref == self
}
}
impl PartialEq<LispBuffer> for OpenBuffer<'_> {
fn eq(&self, other: &LispBuffer) -> bool {
self.back_ref == other
}
}
impl Eq for LispBufferInner {}
impl Display for LispBufferInner {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let data = self.text_buffer.lock().unwrap();
let name = match data.as_ref() {
Some(buf) => &buf.name,
None => "deleted buffer",
};
write!(f, "#<{name}>")
}
}
impl Trace for LispBufferInner {
fn trace(&self, _v: &mut GcState) {
}
}
impl<'new> LispBuffer {
pub(in crate::core) fn clone_in<const C: bool>(
&self,
_: &'new Block<C>,
) -> Gc<&'new LispBuffer> {
unsafe { self.with_lifetime().tag() }
}
}