flams_utils/sourcerefs/mod.rs
1mod convert;
2mod linecol;
3//mod lineendings;
4mod offsets;
5mod ranges;
6
7pub use linecol::*;
8//pub use lineendings::*;
9pub use offsets::*;
10pub use ranges::*;
11
12use crate::CondSerialize;
13use std::fmt::Debug;
14
15pub type LSPLineCol = LineCol<Utf16Offset>;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum PositionKind {
19 Byte,
20 Char,
21 Utf16,
22 LineColByte,
23 LineColChar,
24 LineColUtf16,
25 None,
26}
27
28#[allow(clippy::inline_always)]
29pub trait StringPosition:
30 std::fmt::Debug
31 + std::fmt::Display
32 + Clone
33 + Copy
34 + PartialEq
35 + Eq
36 + PartialOrd
37 + Ord
38 + Default
39 + std::hash::Hash
40 + std::ops::Add<Output = Self>
41 + std::ops::AddAssign
42 + std::ops::Sub<Output = Self>
43 + std::ops::SubAssign
44 + crate::__private::Sealed
45 + CondSerialize
46 + 'static
47{
48 const KIND: PositionKind;
49 fn from_other<P: StringPosition>(p: P, source: &str) -> Self;
50
51 #[inline(always)]
52 #[must_use]
53 fn from_many<P: StringPosition>(source: &str) -> PositionConverter<'_, P, Self> {
54 PositionConverter::new(source)
55 }
56
57 fn len(s: &str) -> Self;
58
59 #[inline(always)]
60 fn into_other<P: StringPosition>(self, source: &str) -> P {
61 P::from_other(self, source)
62 }
63
64 #[inline(always)]
65 #[must_use]
66 fn into_many<P: StringPosition>(source: &str) -> PositionConverter<'_, Self, P> {
67 PositionConverter::new(source)
68 }
69 fn get_range(start: Self, end: Self, text: &str) -> Option<&str> {
70 let off =
71 PositionConverter::<Self, ByteOffset>::new(text).next_range(StringRange { start, end });
72 text.get(off.start.0..off.end.0)
73 }
74
75 fn inc_offset_by(&mut self, text: &str);
76 fn inc_by(&mut self, c: char);
77 fn inc_newline(&mut self, crlf: bool);
78}
79
80/// Convert multiple successive positions or ranges in a string.
81///
82/// More performant
83/// than calling [`StringPosition::from_other`] for every single one, since
84/// we can only need to consider the not already covered part of the string.
85pub struct PositionConverter<'s, From: StringPosition, To: StringPosition> {
86 source: &'s str,
87 from: From,
88 to: To,
89}
90impl<'s, From: StringPosition, To: StringPosition> PositionConverter<'s, From, To> {
91 #[must_use]
92 #[inline]
93 pub fn new(source: &'s str) -> Self {
94 Self {
95 source,
96 from: From::default(),
97 to: To::default(),
98 }
99 }
100
101 #[inline]
102 pub const fn current_position(&self) -> To {
103 self.to
104 }
105
106 #[inline]
107 pub fn next_range(&mut self, range: StringRange<From>) -> StringRange<To> {
108 StringRange {
109 start: self.next(range.start),
110 end: self.next(range.end),
111 }
112 }
113
114 #[inline]
115 pub fn next(&mut self, pos: From) -> To {
116 let r = unsafe {
117 match (From::KIND, To::KIND) {
118 (p, o) if p == o => {
119 self.from = pos;
120 self.to = std::mem::transmute_copy(&pos);
121 return self.to;
122 }
123 (PositionKind::Byte, _) => {
124 self.next_i(std::mem::transmute_copy::<_, ByteOffset>(&(pos - self.from)).0)
125 }
126 (_, PositionKind::Byte) => {
127 let r: ByteOffset = (pos - self.from).into_other(self.source);
128 self.source = &self.source[r.0..];
129 std::mem::transmute_copy(&r)
130 }
131 _ => self.next_i((pos - self.from).into_other::<ByteOffset>(self.source).0),
132 }
133 };
134 self.from = pos;
135 self.to += r;
136 self.to
137 }
138 fn next_i(&mut self, offset: usize) -> To {
139 let r = To::len(&self.source[..offset]);
140 self.source = &self.source[offset..];
141 r
142 }
143}
144#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
145#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
146pub struct NoPosition;
147impl crate::__private::Sealed for NoPosition {}
148impl std::fmt::Display for NoPosition {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 Ok(())
151 }
152}
153impl StringPosition for NoPosition {
154 const KIND: PositionKind = PositionKind::None;
155 #[inline]
156 fn from_other<P: StringPosition>(_: P, _: &str) -> Self {
157 Self
158 }
159 #[inline]
160 fn len(_: &str) -> Self {
161 Self
162 }
163 #[inline(always)]
164 fn inc_by(&mut self, _: char) {}
165 #[inline(always)]
166 fn inc_newline(&mut self, _: bool) {}
167 #[inline(always)]
168 fn inc_offset_by(&mut self, _: &str) {}
169}
170impl std::ops::Add for NoPosition {
171 type Output = Self;
172 #[inline]
173 fn add(self, _: Self) -> Self::Output {
174 Self
175 }
176}
177impl std::ops::AddAssign for NoPosition {
178 #[inline(always)]
179 fn add_assign(&mut self, _: Self) {}
180}
181impl std::ops::Sub for NoPosition {
182 type Output = Self;
183 #[inline]
184 fn sub(self, _: Self) -> Self::Output {
185 Self
186 }
187}
188impl std::ops::SubAssign for NoPosition {
189 #[inline(always)]
190 fn sub_assign(&mut self, _: Self) {}
191}
192
193/*
194pub trait StringPosition:
195 Clone + Copy + Default + Debug + PartialOrd + Ord + 'static + CondSerialize
196{
197 fn inc_by(&mut self, c: char);
198 fn inc_newline(&mut self, crlf: bool);
199 //fn update_str_no_newline(&mut self, s: &str);
200 //fn update_str_maybe_newline(&mut self, s: &str);
201 //fn get_range(start: Self, end: Self, text: &str) -> Option<&str>;
202}
203impl StringPosition for () {
204 #[inline]
205 fn inc_by(&mut self, _: char) {}
206 #[inline]
207 fn inc_newline(&mut self, _: bool) {}
208 /*#[inline]
209 fn update_str_no_newline(&mut self, _: &str) {}
210 #[inline]
211 fn update_str_maybe_newline(&mut self, _: &str) {}
212 #[inline]
213 fn get_range(start: Self, end: Self, text: &str) -> Option<&str> {
214 None
215 }*/
216}
217
218#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220pub struct ByteOffset(usize);
221impl Display for ByteOffset {
222 #[inline]
223 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
224 self.0.fmt(f)
225 }
226}
227impl Debug for ByteOffset {
228 #[inline]
229 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
230 <Self as Display>::fmt(self, f)
231 }
232}
233impl StringPosition for ByteOffset {
234 #[inline]
235 fn inc_by(&mut self, c: char) {
236 self.offset += c.len_utf8();
237 }
238 #[inline]
239 fn inc_newline(&mut self, rn: bool) {
240 self.offset += if rn { 2 } else { 1 };
241 }
242 /*
243 #[inline]
244 fn update_str_no_newline(&mut self, s: &str) {
245 self.offset += s.len();
246 }
247 #[inline]
248 fn update_str_maybe_newline(&mut self, s: &str) {
249 self.update_str_no_newline(s);
250 }
251 fn get_range(start: Self, end: Self, text: &str) -> Option<&str> {
252 text.get(start.offset..end.offset)
253 }
254 */
255}
256
257#[derive(Clone, Copy, PartialEq, Eq, Default)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259pub struct LSPLineCol {
260 pub line: u32,
261 pub col: u32,
262}
263impl Ord for LSPLineCol {
264 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
265 self.line.cmp(&other.line).then(self.col.cmp(&other.col))
266 }
267}
268impl PartialOrd for LSPLineCol {
269 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
270 Some(self.cmp(other))
271 }
272}
273
274impl Display for LSPLineCol {
275 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
276 write!(f, "l. {} c. {}", self.line, self.col)
277 }
278}
279impl Debug for LSPLineCol {
280 #[inline]
281 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
282 <Self as Display>::fmt(self, f)
283 }
284}
285impl LSPLineCol {
286 // TODO use UTF16 instead of UTF8
287 pub fn get_range_offsets(start: Self, end: Self, text: &str) -> (usize, usize) {
288 let Self {
289 line: mut start_line,
290 col: startc,
291 } = start;
292 let Self {
293 line: mut end_line,
294 col: mut endc,
295 } = end;
296 if start_line == end_line {
297 endc -= startc;
298 }
299 end_line -= start_line;
300 let mut start = 0;
301 let mut rest = text;
302 while start_line > 0 {
303 if let Some(i) = rest.find(['\n', '\r']) {
304 start += i + 1;
305 if rest.as_bytes()[i] == b'\r' && rest.as_bytes().get(i + 1) == Some(&b'\n') {
306 start += 1;
307 rest = &rest[i + 2..];
308 } else {
309 rest = &rest[i + 1..];
310 }
311 start_line -= 1;
312 } else {
313 start = text.len();
314 rest = "";
315 end_line = 0;
316 break;
317 }
318 }
319
320 let next = rest
321 .chars()
322 .take(startc as usize)
323 .map(char::len_utf8)
324 .sum::<usize>();
325 start += next;
326 rest = &rest[next..];
327
328 let mut end = start;
329 while end_line > 0 {
330 if let Some(i) = rest.find(['\n', '\r']) {
331 end += i + 1;
332 if rest.as_bytes()[i] == b'\r' && rest.as_bytes().get(i + 1) == Some(&b'\n') {
333 end += 1;
334 rest = &rest[i + 2..];
335 } else {
336 rest = &rest[i + 1..];
337 }
338 end_line -= 1;
339 } else {
340 end = text.len();
341 rest = "";
342 break;
343 }
344 }
345 end += rest
346 .chars()
347 .take(endc as usize)
348 .map(char::len_utf8)
349 .sum::<usize>();
350 (start, end)
351 }
352}
353impl StringPosition for LSPLineCol {
354 #[inline]
355 #[allow(clippy::cast_possible_truncation)]
356 fn inc_by(&mut self, c: char) {
357 self.col += c.len_utf16() as u32;
358 }
359 #[inline]
360 fn inc_newline(&mut self, _: bool) {
361 self.line += 1;
362 self.col = 0;
363 }
364 /*
365 #[inline]
366 #[allow(clippy::cast_possible_truncation)]
367 fn update_str_no_newline(&mut self, s: &str) {
368 self.col += s.chars().map(|c| char::len_utf16(c) as u32).sum::<u32>();
369 }
370
371 #[allow(clippy::cast_possible_truncation)]
372 fn update_str_maybe_newline(&mut self, s: &str) {
373 let s = s.split("\r\n").flat_map(|s| s.split(['\n', '\r']));
374 let mut last = "";
375 let mut first = true;
376 for l in s {
377 if first {
378 first = false;
379 } else {
380 self.line += 1;
381 self.col = 0;
382 }
383 last = l;
384 }
385 self.col += last.chars().map(|c| char::len_utf16(c) as u32).sum::<u32>();
386 }
387 fn get_range(start: Self, end: Self, text: &str) -> Option<&str> {
388 let (nstart, nend) = Self::get_range_offsets(start, end, text);
389 if nstart == nend {
390 Some("")
391 } else if nend < text.len() {
392 text.get(nstart..nend)
393 } else {
394 None
395 }
396 }
397 */
398}
399
400impl<A: StringPosition, B: StringPosition> StringPosition for (A, B) {
401 #[inline]
402 fn inc_by(&mut self, c: char) {
403 self.0.inc_by(c);
404 self.1.inc_by(c);
405 }
406 #[inline]
407 fn inc_newline(&mut self, rn: bool) {
408 self.0.inc_newline(rn);
409 self.1.inc_newline(rn);
410 }
411 /*
412 #[inline]
413 fn update_str_no_newline(&mut self, s: &str) {
414 self.0.update_str_no_newline(s);
415 self.1.update_str_no_newline(s);
416 }
417 #[inline]
418 fn update_str_maybe_newline(&mut self, s: &str) {
419 self.0.update_str_maybe_newline(s);
420 self.1.update_str_maybe_newline(s);
421 }
422 fn get_range(start: Self, end: Self, text: &str) -> Option<&str> {
423 A::get_range(start.0, end.0, text)
424 }
425 */
426}
427 */
428
429/*
430#[derive(Clone, Copy, PartialEq, Eq, Default)]
431#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
432pub struct StringRange<P: StringPosition> {
433 pub start: P,
434 pub end: P,
435}
436impl<P: StringPosition> Display for StringRange<P> {
437 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
438 write!(f, "{:#?}-{:#?}", self.start, self.end)
439 }
440}
441impl<P: StringPosition> Debug for StringRange<P> {
442 #[inline]
443 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
444 <Self as Display>::fmt(self, f)
445 }
446}
447impl<P: StringPosition> StringRange<P> {
448 pub fn contains(&self, pos: P) -> bool {
449 self.start <= pos && pos <= self.end
450 }
451}
452 */
453
454#[test]
455fn test() {
456 let str = "\n\n";
457 let len = str
458 .split("\r\n")
459 .flat_map(|s| s.split(['\r', '\n']))
460 .count();
461 assert_eq!(len, 3);
462}