1use crate::source::{Source, TextChunk, TextEdit};
2use core::range::Range;
3use std::borrow::Cow;
4use triomphe::Arc;
5use url::Url;
6
7const CHUNK_SIZE: usize = 4096;
8
9#[derive(Clone, Debug)]
11pub struct RopeSource {
12 url: Option<Url>,
13 chunks: Arc<[Arc<str>]>,
14 starts: Arc<[usize]>,
15 len: usize,
16}
17
18#[derive(Clone, Debug)]
20pub struct RopeBuffer {
21 url: Option<Url>,
22 chunks: Vec<Arc<str>>,
23 starts: Vec<usize>,
24 len: usize,
25}
26
27impl RopeBuffer {
28 pub fn new(input: impl ToString) -> Self {
30 Self::new_with_url(input, None)
31 }
32
33 pub fn new_with_url(input: impl ToString, url: impl Into<Option<Url>>) -> Self {
35 let url = url.into();
36 let text = input.to_string();
37 let chunks = chunkify(&text);
38 let (starts, len) = rebuild_starts(&chunks);
39 Self { url, chunks, starts, len }
40 }
41
42 pub fn snapshot(&self) -> RopeSource {
44 RopeSource { url: self.url.clone(), chunks: Arc::<[Arc<str>]>::from(self.chunks.clone()), starts: Arc::<[usize]>::from(self.starts.clone()), len: self.len }
45 }
46
47 pub fn apply_edits_range(&mut self, edits: &[TextEdit]) -> Range<usize> {
49 let old_len = self.len;
50 if edits.is_empty() {
51 return Range { start: old_len, end: old_len };
52 }
53
54 let mut order: Vec<usize> = (0..edits.len()).collect();
55 order.sort_by_key(|&i| edits[i].span.start);
56
57 let mut reparse_from = old_len;
58 let mut reparse_to = 0;
59 let mut delta: isize = 0;
60 for &i in &order {
61 let TextEdit { span, text } = &edits[i];
62 reparse_from = reparse_from.min(span.start);
63 let start_new = (span.start as isize + delta) as usize;
64 let end_new = start_new + text.len();
65 reparse_to = reparse_to.max(end_new);
66 delta += text.len() as isize - (span.end - span.start) as isize;
67 }
68
69 for &i in order.iter().rev() {
70 let TextEdit { span, text } = &edits[i];
71 self.replace_range(Range { start: span.start, end: span.end }, text);
72 }
73
74 Range { start: reparse_from, end: reparse_to }
75 }
76
77 pub fn apply_edits(&mut self, edits: &[TextEdit]) -> usize {
79 self.apply_edits_range(edits).start
80 }
81
82 fn replace_range(&mut self, span: Range<usize>, replacement: &str) {
83 let start = span.start.min(self.len);
84 let end = span.end.min(self.len).max(start);
85
86 self.split_at(start);
87 self.split_at(end);
88
89 let start_idx = self.boundary_index(start);
90 let end_idx = self.boundary_index(end);
91 if start_idx < end_idx {
92 self.chunks.drain(start_idx..end_idx);
93 }
94
95 if !replacement.is_empty() {
96 let new_chunks = chunkify(replacement);
97 let insert_at = start_idx.min(self.chunks.len());
98 self.chunks.splice(insert_at..insert_at, new_chunks);
99 }
100
101 let (starts, len) = rebuild_starts(&self.chunks);
102 self.starts = starts;
103 self.len = len;
104 }
105
106 fn split_at(&mut self, offset: usize) {
107 if offset == 0 || offset >= self.len {
108 return;
109 }
110 let (idx, start) = self.chunk_index(offset);
111 let rel = offset - start;
112 let s = self.chunks[idx].clone();
113 let s = s.as_ref();
114 if rel == 0 || rel >= s.len() {
115 return;
116 }
117 let Some(left) = s.get(..rel)
118 else {
119 return;
120 };
121 let Some(right) = s.get(rel..)
122 else {
123 return;
124 };
125 if left.is_empty() || right.is_empty() {
126 return;
127 }
128 self.chunks[idx] = Arc::<str>::from(left.to_string());
129 self.chunks.insert(idx + 1, Arc::<str>::from(right.to_string()));
130 let (starts, len) = rebuild_starts(&self.chunks);
131 self.starts = starts;
132 self.len = len;
133 }
134
135 fn boundary_index(&self, offset: usize) -> usize {
136 if offset >= self.len {
137 return self.chunks.len();
138 }
139 match self.starts.binary_search(&offset) {
140 Ok(i) => i,
141 Err(i) => i,
142 }
143 }
144
145 fn chunk_index(&self, offset: usize) -> (usize, usize) {
146 let offset = offset.min(self.len.saturating_sub(1));
147 match self.starts.binary_search(&offset) {
148 Ok(i) => (i, self.starts[i]),
149 Err(0) => (0, 0),
150 Err(i) => (i - 1, self.starts[i - 1]),
151 }
152 }
153}
154
155impl Source for RopeBuffer {
156 fn length(&self) -> usize {
157 self.len
158 }
159
160 fn chunk_at(&self, offset: usize) -> TextChunk<'_> {
161 if offset >= self.len {
162 return TextChunk { start: self.len, text: "" };
163 }
164 let (idx, start) = self.chunk_index(offset);
165 TextChunk { start, text: self.chunks[idx].as_ref() }
166 }
167
168 fn get_text_in(&self, range: Range<usize>) -> Cow<'_, str> {
169 text_in_chunks(&self.chunks, &self.starts, self.len, range)
170 }
171
172 fn get_url(&self) -> Option<&Url> {
173 self.url.as_ref()
174 }
175}
176
177impl Source for RopeSource {
178 fn length(&self) -> usize {
179 self.len
180 }
181
182 fn chunk_at(&self, offset: usize) -> TextChunk<'_> {
183 if offset >= self.len {
184 return TextChunk { start: self.len, text: "" };
185 }
186 let idx = match self.starts.binary_search(&offset) {
187 Ok(i) => i,
188 Err(0) => 0,
189 Err(i) => i - 1,
190 };
191 let start = self.starts[idx];
192 TextChunk { start, text: self.chunks[idx].as_ref() }
193 }
194
195 fn get_text_in(&self, range: Range<usize>) -> Cow<'_, str> {
196 text_in_chunks(&self.chunks, &self.starts, self.len, range)
197 }
198
199 fn get_url(&self) -> Option<&Url> {
200 self.url.as_ref()
201 }
202}
203
204fn rebuild_starts(chunks: &[Arc<str>]) -> (Vec<usize>, usize) {
205 let mut starts = Vec::with_capacity(chunks.len());
206 let mut offset = 0usize;
207 for c in chunks {
208 starts.push(offset);
209 offset += c.len();
210 }
211 (starts, offset)
212}
213
214fn chunkify(text: &str) -> Vec<Arc<str>> {
215 if text.is_empty() {
216 return vec![];
217 }
218 let mut out = Vec::new();
219 let mut start = 0usize;
220 while start < text.len() {
221 let mut end = (start + CHUNK_SIZE).min(text.len());
222 while end > start && !text.is_char_boundary(end) {
223 end -= 1;
224 }
225 if end == start {
226 end = text.len();
227 }
228 let part = &text[start..end];
229 out.push(Arc::<str>::from(part.to_string()));
230 start = end;
231 }
232 out
233}
234
235fn text_in_chunks<'a>(chunks: &'a [Arc<str>], starts: &'a [usize], len: usize, range: Range<usize>) -> Cow<'a, str> {
236 if range.start >= range.end || range.start >= len {
237 return Cow::Borrowed("");
238 }
239 let start = range.start;
240 let end = range.end.min(len);
241
242 let start_idx = match starts.binary_search(&start) {
243 Ok(i) => i,
244 Err(0) => 0,
245 Err(i) => i - 1,
246 };
247 let end_idx = match starts.binary_search(&end) {
248 Ok(i) => i,
249 Err(0) => 0,
250 Err(i) => i - 1,
251 };
252
253 if start_idx == end_idx {
254 let base = starts[start_idx];
255 let rel_start = start - base;
256 let rel_end = end - base;
257 let s = chunks[start_idx].as_ref();
258 return s.get(rel_start..rel_end).map(Cow::Borrowed).unwrap_or(Cow::Borrowed(""));
259 }
260
261 let mut buf = String::new();
262 for (i, c) in chunks.iter().enumerate().skip(start_idx).take(end_idx - start_idx + 1) {
263 let base = starts[i];
264 let cs = c.as_ref();
265 let seg_start = if i == start_idx { start.saturating_sub(base) } else { 0 };
266 let seg_end = if i == end_idx { end.saturating_sub(base) } else { cs.len() };
267 if let Some(seg) = cs.get(seg_start..seg_end) {
268 buf.push_str(seg);
269 }
270 }
271 Cow::Owned(buf)
272}