1pub mod history;
5
6use history::{Edit, EditKind, History};
7use ropey::Rope;
8
9pub struct Buffer {
11 pub rope: Rope,
12 pub path: Option<String>,
13 pub dirty: bool,
14 pub epoch: u64,
16 pub readonly: bool,
18 pub name: Option<String>,
21 pub history: History,
24 pub replaying: bool,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct Range {
31 pub start: usize,
32 pub end: usize,
33 pub linewise: bool,
36}
37
38impl Range {
39 pub fn charwise(start: usize, end: usize) -> Self {
40 debug_assert!(start <= end);
41 Self {
42 start,
43 end,
44 linewise: false,
45 }
46 }
47 pub fn linewise(start: usize, end: usize) -> Self {
48 debug_assert!(start <= end);
49 Self {
50 start,
51 end,
52 linewise: true,
53 }
54 }
55 pub fn len(&self) -> usize {
56 self.end - self.start
57 }
58 pub fn is_empty(&self) -> bool {
59 self.start == self.end
60 }
61}
62
63impl Buffer {
64 pub fn from_text(text: &str) -> Self {
65 Self {
66 rope: Rope::from_str(text),
67 path: None,
68 dirty: false,
69 epoch: 0,
70 readonly: false,
71 name: None,
72 history: History::default(),
73 replaying: false,
74 }
75 }
76
77 pub fn open(path: &str) -> std::io::Result<Self> {
80 let text = match std::fs::read_to_string(path) {
81 Ok(t) => t,
82 Err(e) if e.kind() == std::io::ErrorKind::NotFound => String::new(),
83 Err(e) => return Err(e),
84 };
85 Ok(Self {
86 rope: Rope::from_str(&text),
87 path: Some(path.to_string()),
88 dirty: false,
89 epoch: 0,
90 readonly: false,
91 name: None,
92 history: History::default(),
93 replaying: false,
94 })
95 }
96
97 pub fn save(&mut self) -> std::io::Result<()> {
98 if let Some(path) = &self.path {
99 std::fs::write(path, self.rope.to_string())?;
100 self.dirty = false;
101 }
102 Ok(())
103 }
104
105 pub fn len_bytes(&self) -> usize {
106 self.rope.len_bytes()
107 }
108 pub fn len_lines(&self) -> usize {
109 self.rope.len_lines()
110 }
111
112 pub fn last_content_line(&self) -> usize {
115 let mut l = self.len_lines().saturating_sub(1);
116 if self.len_bytes() > 0 && self.byte(self.len_bytes() - 1) == b'\n' && l > 0 {
117 l -= 1;
118 }
119 l
120 }
121
122 pub fn line_start(&self, line: usize) -> usize {
124 self.rope
125 .line_to_byte(line.min(self.len_lines().saturating_sub(1)))
126 }
127
128 pub fn line_end(&self, line: usize) -> usize {
130 let start = self.line_start(line);
131 let mut end = self.line_start((line + 1).min(self.len_lines().saturating_sub(1)));
132 if line + 1 >= self.len_lines() {
133 end = self.len_bytes();
134 }
135 if end > start && self.byte(end - 1) == b'\n' {
137 end -= 1;
138 }
139 end
140 }
141
142 pub fn line_of(&self, offset: usize) -> usize {
143 self.rope.byte_to_line(offset.min(self.len_bytes()))
144 }
145
146 pub fn col_of(&self, offset: usize) -> usize {
148 offset - self.line_start(self.line_of(offset))
149 }
150
151 pub fn byte(&self, offset: usize) -> u8 {
152 self.rope
153 .byte(offset.min(self.len_bytes().saturating_sub(1)))
154 }
155
156 pub fn byte_at(&self, offset: usize) -> Option<u8> {
157 if offset < self.len_bytes() {
158 Some(self.rope.byte(offset))
159 } else {
160 None
161 }
162 }
163
164 pub fn is_boundary(&self, offset: usize) -> bool {
169 if offset == 0 || offset == self.len_bytes() {
170 return true;
171 }
172 if offset > self.len_bytes() {
173 return false;
174 }
175 match self.rope.try_byte_to_char(offset) {
176 Ok(c) => self.rope.try_char_to_byte(c).is_ok_and(|b| b == offset),
177 Err(_) => false,
178 }
179 }
180
181 pub fn clamp_boundary(&self, mut offset: usize) -> usize {
184 offset = offset.min(self.len_bytes());
185 while offset > 0 && !self.is_boundary(offset) {
186 offset -= 1;
187 }
188 offset
189 }
190
191 pub fn ceil_boundary(&self, mut offset: usize) -> usize {
196 offset = offset.min(self.len_bytes());
197 while offset < self.len_bytes() && !self.is_boundary(offset) {
198 offset += 1;
199 }
200 offset
201 }
202
203 pub fn slice_string(&self, range: Range) -> String {
206 let start = range.start.min(self.len_bytes());
207 let end = range.end.min(self.len_bytes());
208 self.rope.byte_slice(start..end.max(start)).to_string()
209 }
210
211 pub fn apply_history(&mut self, ops: Vec<Edit>) {
213 self.replaying = true;
214 for op in ops {
215 match op.kind {
216 EditKind::Insert => {
217 let at = self.clamp_boundary(op.at.min(self.len_bytes()));
218 self.rope.insert(self.rope.byte_to_char(at), &op.text);
219 }
220 EditKind::Delete => {
221 let end = self.clamp_boundary((op.at + op.text.len()).min(self.len_bytes()));
224 let start = self.clamp_boundary(op.at.min(end));
225 if start < end {
226 self.rope
227 .remove(self.rope.byte_to_char(start)..self.rope.byte_to_char(end));
228 }
229 }
230 }
231 }
232 self.replaying = false;
233 self.dirty = true;
234 self.epoch += 1;
235 }
236
237 pub fn replace_all(&mut self, text: &str) {
239 self.rope = Rope::from_str(text);
240 self.epoch += 1;
241 }
242
243 pub fn delete(&mut self, range: Range) -> String {
245 let start = self.clamp_boundary(range.start.min(self.len_bytes()));
247 let end = self.clamp_boundary(range.end.min(self.len_bytes()));
248 if start >= end {
249 return String::new();
250 }
251 let text = self.rope.byte_slice(start..end).to_string();
252 let cstart = self.rope.byte_to_char(start);
254 let cend = self.rope.byte_to_char(end);
255 self.rope.remove(cstart..cend);
256 self.dirty = true;
257 self.epoch += 1;
258 if !self.replaying && !self.readonly {
259 self.history.record(
260 Edit {
261 at: range.start,
262 text: text.clone(),
263 kind: EditKind::Insert,
264 },
265 Edit {
266 at: range.start,
267 text: text.clone(),
268 kind: EditKind::Delete,
269 },
270 );
271 }
272 text
273 }
274
275 pub fn insert(&mut self, at: usize, text: &str) {
276 let at = self.clamp_boundary(at);
277 self.rope.insert(self.rope.byte_to_char(at), text);
278 self.dirty = true;
279 self.epoch += 1;
280 if !self.replaying && !self.readonly {
281 self.history.record(
282 Edit {
283 at,
284 text: text.into(),
285 kind: EditKind::Delete,
286 },
287 Edit {
288 at,
289 text: text.into(),
290 kind: EditKind::Insert,
291 },
292 );
293 }
294 }
295
296 pub fn line_text(&self, line: usize) -> String {
297 let start = self.line_start(line);
298 let end = self.line_end(line);
299 self.rope.byte_slice(start..end).to_string()
300 }
301}