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 clamp_boundary(&self, mut offset: usize) -> usize {
167 offset = offset.min(self.len_bytes());
168 while offset > 0 && self.rope.try_byte_to_char(offset).is_err() {
169 offset -= 1;
170 }
171 offset
172 }
173
174 pub fn slice_string(&self, range: Range) -> String {
176 self.rope.byte_slice(range.start..range.end).to_string()
177 }
178
179 pub fn apply_history(&mut self, ops: Vec<Edit>) {
181 self.replaying = true;
182 for op in ops {
183 match op.kind {
184 EditKind::Insert => {
185 let at = self.clamp_boundary(op.at.min(self.len_bytes()));
186 self.rope.insert(at, &op.text);
187 }
188 EditKind::Delete => {
189 let end = self.clamp_boundary((op.at + op.text.len()).min(self.len_bytes()));
192 let start = self.clamp_boundary(op.at.min(end));
193 if start < end {
194 self.rope.remove(start..end);
195 }
196 }
197 }
198 }
199 self.replaying = false;
200 self.dirty = true;
201 self.epoch += 1;
202 }
203
204 pub fn replace_all(&mut self, text: &str) {
206 self.rope = Rope::from_str(text);
207 self.epoch += 1;
208 }
209
210 pub fn delete(&mut self, range: Range) -> String {
212 let text = self.slice_string(range);
213 self.rope.remove(range.start..range.end);
214 self.dirty = true;
215 self.epoch += 1;
216 if !self.replaying && !self.readonly {
217 self.history.record(
218 Edit {
219 at: range.start,
220 text: text.clone(),
221 kind: EditKind::Insert,
222 },
223 Edit {
224 at: range.start,
225 text: text.clone(),
226 kind: EditKind::Delete,
227 },
228 );
229 }
230 text
231 }
232
233 pub fn insert(&mut self, at: usize, text: &str) {
234 let at = self.clamp_boundary(at);
235 self.rope.insert(at, text);
236 self.dirty = true;
237 self.epoch += 1;
238 if !self.replaying && !self.readonly {
239 self.history.record(
240 Edit {
241 at,
242 text: text.into(),
243 kind: EditKind::Delete,
244 },
245 Edit {
246 at,
247 text: text.into(),
248 kind: EditKind::Insert,
249 },
250 );
251 }
252 }
253
254 pub fn line_text(&self, line: usize) -> String {
255 let start = self.line_start(line);
256 let end = self.line_end(line);
257 self.rope.byte_slice(start..end).to_string()
258 }
259}