1 //! Enhances `ide::LineIndex` with additional info required to convert offsets
2 //! into lsp positions.
4 //! We maintain invariant that all internal strings use `\n` as line separator.
5 //! This module does line ending conversion and detection (so that we can
6 //! convert back to `\r\n` on the way out).
10 pub enum OffsetEncoding {
15 pub(crate) struct LineIndex {
16 pub(crate) index: Arc<ide::LineIndex>,
17 pub(crate) endings: LineEndings,
18 pub(crate) encoding: OffsetEncoding,
21 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22 pub(crate) enum LineEndings {
28 /// Replaces `\r\n` with `\n` in-place in `src`.
29 pub(crate) fn normalize(src: String) -> (String, LineEndings) {
30 if !src.as_bytes().contains(&b'\r') {
31 return (src, LineEndings::Unix);
34 // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding.
35 // While we *can* call `as_mut_vec` and do surgery on the live string
36 // directly, let's rather steal the contents of `src`. This makes the code
37 // safe even if a panic occurs.
39 let mut buf = src.into_bytes();
41 let mut tail = buf.as_mut_slice();
43 let idx = match find_crlf(&tail[gap_len..]) {
45 Some(idx) => idx + gap_len,
47 tail.copy_within(gap_len..idx, 0);
48 tail = &mut tail[idx - gap_len..];
49 if tail.len() == gap_len {
55 // Account for removed `\r`.
56 // After `set_len`, `buf` is guaranteed to contain utf-8 again.
57 let new_len = buf.len() - gap_len;
60 String::from_utf8_unchecked(buf)
62 return (src, LineEndings::Dos);
64 fn find_crlf(src: &[u8]) -> Option<usize> {
65 src.windows(2).position(|it| it == b"\r\n")