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