Skip to main content

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}