rune/core/object/
buffer.rs
1use super::{Gc, Object, ObjectType, TagType, WithLifetime};
2use crate::{
3 core::{
4 error::{Type, TypeError},
5 gc::{Block, Context, GcHeap, GcState, Trace},
6 },
7 derive_GcMoveable,
8 intervals::IntervalTree,
9};
10use anyhow::{Result, bail};
11use rune_macros::Trace;
12use std::{
13 fmt::Display,
14 ops::{Deref, DerefMut},
15 sync::{Mutex, MutexGuard},
16};
17use text_buffer::Buffer as TextBuffer;
18
19#[derive(Debug)]
21pub(crate) struct OpenBuffer<'a> {
22 data: MutexGuard<'a, Option<BufferData>>,
23 back_ref: &'a LispBuffer,
24}
25
26impl OpenBuffer<'_> {
27 pub(crate) fn get(&self) -> &BufferData {
28 self.data.as_ref().unwrap()
30 }
31
32 pub(crate) fn get_mut(&mut self) -> &mut BufferData {
33 self.data.as_mut().unwrap()
35 }
36
37 pub(crate) fn kill(&mut self) -> bool {
39 let killed = self.data.is_some();
40 *self.data = None;
41 killed
42 }
43
44 pub(crate) fn lisp_buffer<'ob>(&self, cx: &'ob Context) -> &'ob LispBuffer {
45 cx.bind(self.back_ref)
46 }
47
48 pub(crate) fn insert(&mut self, arg: Object) -> Result<()> {
49 match arg.untag() {
50 ObjectType::Int(i) => {
51 let Ok(u_32) = i.try_into() else { bail!("{i} is an invalid char") };
52 let Some(chr) = char::from_u32(u_32) else { bail!("{i} is an Invalid char") };
53 self.get_mut().text.insert_char(chr);
54 }
55 ObjectType::String(s) => self.get_mut().text.insert(s),
56 x => bail!(TypeError::new(Type::String, x)),
57 }
58 Ok(())
59 }
60
61 pub(crate) fn slice_with_gap(&self, beg: usize, end: usize) -> Result<(&str, &str)> {
62 let beg = self.in_range(beg)?;
63 let end = self.in_range(end)?;
64 Ok(self.get().text.slice(beg..end))
65 }
66
67 pub(crate) fn delete(&mut self, beg: usize, end: usize) -> Result<()> {
68 let beg = self.in_range(beg)?;
69 let end = self.in_range(end)?;
70 self.get_mut().text.delete_range(beg, end);
71 Ok(())
72 }
73
74 fn in_range(&self, pos: usize) -> Result<usize> {
75 if pos == 0 || pos > self.get().text.len_chars() + 1 {
76 bail!("Position {pos} out of range in {}", self.get().name);
77 }
78 Ok(pos - 1)
79 }
80}
81
82impl<'new> WithLifetime<'new> for OpenBuffer<'_> {
83 type Out = OpenBuffer<'new>;
84
85 unsafe fn with_lifetime(self) -> Self::Out {
86 std::mem::transmute(self)
87 }
88}
89
90impl PartialEq<str> for OpenBuffer<'_> {
91 fn eq(&self, other: &str) -> bool {
92 self.get().text == other
93 }
94}
95
96impl Deref for OpenBuffer<'_> {
97 type Target = BufferData;
98
99 fn deref(&self) -> &Self::Target {
100 self.get()
101 }
102}
103
104impl DerefMut for OpenBuffer<'_> {
105 fn deref_mut(&mut self) -> &mut Self::Target {
106 self.get_mut()
107 }
108}
109
110#[derive(Debug)]
113pub(crate) struct BufferData {
114 pub(crate) name: String,
115 pub(crate) text: TextBuffer,
116 pub(crate) textprops: IntervalTree<'static>,
117}
118
119impl BufferData {
120 pub fn textprops_with_lifetime<'new>(&mut self) -> &mut IntervalTree<'new> {
121 unsafe { std::mem::transmute(&mut self.textprops) }
122 }
123}
124
125#[derive(Debug)]
126struct LispBufferInner {
127 text_buffer: Mutex<Option<BufferData>>,
128}
129
130#[derive(PartialEq, Eq, Trace)]
133pub(crate) struct LispBuffer(GcHeap<LispBufferInner>);
134
135derive_GcMoveable!(LispBuffer);
136
137impl LispBuffer {
138 pub(crate) fn create(name: String, block: &Block<true>) -> &LispBuffer {
139 let buffer = unsafe { Self::new(name, block) };
140 block.objects.alloc(buffer)
141 }
142
143 pub(crate) unsafe fn new(name: String, _: &Block<true>) -> LispBuffer {
144 let textprops = IntervalTree::new();
145 let new = LispBufferInner {
146 text_buffer: Mutex::new(Some(BufferData { name, text: TextBuffer::new(), textprops })),
147 };
148 Self(GcHeap::new(new, true))
149 }
150
151 pub(crate) fn lock(&self) -> Result<OpenBuffer<'_>> {
152 let guard = self.0.text_buffer.lock().unwrap();
153 if guard.is_none() {
154 bail!("selecting deleted buffer");
155 }
156 Ok(OpenBuffer { data: guard, back_ref: self })
157 }
158}
159
160impl PartialEq for LispBufferInner {
161 fn eq(&self, other: &Self) -> bool {
162 std::ptr::eq(self, other)
163 }
164}
165
166impl PartialEq<OpenBuffer<'_>> for LispBuffer {
167 fn eq(&self, other: &OpenBuffer) -> bool {
168 other.back_ref == self
169 }
170}
171
172impl PartialEq<LispBuffer> for OpenBuffer<'_> {
173 fn eq(&self, other: &LispBuffer) -> bool {
174 self.back_ref == other
175 }
176}
177
178impl Eq for LispBufferInner {}
179
180impl Display for LispBuffer {
181 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
182 let data = self.0.text_buffer.lock().unwrap();
183 let name = match data.as_ref() {
184 Some(buf) => &buf.name,
185 None => "deleted buffer",
186 };
187 write!(f, "#<{name}>")
188 }
189}
190
191impl std::fmt::Debug for LispBuffer {
192 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
193 Display::fmt(self, f)
194 }
195}
196
197impl Trace for LispBufferInner {
198 fn trace(&self, state: &mut GcState) {
199 let buf = self.text_buffer.lock().unwrap();
201 if let Some(buf) = buf.as_ref() {
202 buf.textprops.trace(state);
203 }
204 }
205}
206
207impl<'new> LispBuffer {
208 pub(in crate::core) fn clone_in<const C: bool>(
209 &self,
210 _: &'new Block<C>,
211 ) -> Gc<&'new LispBuffer> {
212 unsafe { self.with_lifetime().tag() }
213 }
214}