about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/lib')
-rw-r--r--src/tools/rust-analyzer/lib/README.md7
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/lib.rs118
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/map.rs70
-rw-r--r--src/tools/rust-analyzer/lib/line-index/Cargo.toml11
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/lib.rs237
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/tests.rs11
-rw-r--r--src/tools/rust-analyzer/lib/line-index/tests/it.rs62
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/error.rs2
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/lib.rs70
10 files changed, 573 insertions, 19 deletions
diff --git a/src/tools/rust-analyzer/lib/README.md b/src/tools/rust-analyzer/lib/README.md
index 6b2eeac2c0d..ed55e31d6bb 100644
--- a/src/tools/rust-analyzer/lib/README.md
+++ b/src/tools/rust-analyzer/lib/README.md
@@ -1,2 +1,5 @@
-Crates in this directory are published to crates.io and obey semver.
-They *could* live in a separate repo, but we want to experiment with a monorepo setup.
+# lib
+
+Crates in this directory are published to [crates.io](https://crates.io) and obey semver.
+
+They _could_ live in a separate repo, but we want to experiment with a monorepo setup.
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
index ccaaf399176..5107f294394 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs
@@ -4,8 +4,9 @@
 #![warn(missing_docs)]
 
 use std::{
-    fmt,
+    cmp, fmt,
     hash::{Hash, Hasher},
+    iter::{Enumerate, FusedIterator},
     marker::PhantomData,
     ops::{Index, IndexMut, Range, RangeInclusive},
 };
@@ -17,13 +18,27 @@ pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry};
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct RawIdx(u32);
 
+impl RawIdx {
+    /// Constructs a [`RawIdx`] from a u32.
+    pub const fn from_u32(u32: u32) -> Self {
+        RawIdx(u32)
+    }
+
+    /// Deconstructs a [`RawIdx`] into the underlying u32.
+    pub const fn into_u32(self) -> u32 {
+        self.0
+    }
+}
+
 impl From<RawIdx> for u32 {
+    #[inline]
     fn from(raw: RawIdx) -> u32 {
         raw.0
     }
 }
 
 impl From<u32> for RawIdx {
+    #[inline]
     fn from(idx: u32) -> RawIdx {
         RawIdx(idx)
     }
@@ -47,6 +62,18 @@ pub struct Idx<T> {
     _ty: PhantomData<fn() -> T>,
 }
 
+impl<T> Ord for Idx<T> {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
+        self.raw.cmp(&other.raw)
+    }
+}
+
+impl<T> PartialOrd for Idx<T> {
+    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+        self.raw.partial_cmp(&other.raw)
+    }
+}
+
 impl<T> Clone for Idx<T> {
     fn clone(&self) -> Self {
         *self
@@ -79,12 +106,12 @@ impl<T> fmt::Debug for Idx<T> {
 
 impl<T> Idx<T> {
     /// Creates a new index from a [`RawIdx`].
-    pub fn from_raw(raw: RawIdx) -> Self {
+    pub const fn from_raw(raw: RawIdx) -> Self {
         Idx { raw, _ty: PhantomData }
     }
 
     /// Converts this index into the underlying [`RawIdx`].
-    pub fn into_raw(self) -> RawIdx {
+    pub const fn into_raw(self) -> RawIdx {
         self.raw
     }
 }
@@ -147,13 +174,46 @@ impl<T> IdxRange<T> {
     pub fn is_empty(&self) -> bool {
         self.range.is_empty()
     }
+
+    /// Returns the start of the index range.
+    pub fn start(&self) -> Idx<T> {
+        Idx::from_raw(RawIdx::from(self.range.start))
+    }
+
+    /// Returns the end of the index range.
+    pub fn end(&self) -> Idx<T> {
+        Idx::from_raw(RawIdx::from(self.range.end))
+    }
 }
 
 impl<T> Iterator for IdxRange<T> {
     type Item = Idx<T>;
+
     fn next(&mut self) -> Option<Self::Item> {
         self.range.next().map(|raw| Idx::from_raw(raw.into()))
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.range.size_hint()
+    }
+
+    fn count(self) -> usize
+    where
+        Self: Sized,
+    {
+        self.range.count()
+    }
+
+    fn last(self) -> Option<Self::Item>
+    where
+        Self: Sized,
+    {
+        self.range.last().map(|raw| Idx::from_raw(raw.into()))
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.range.nth(n).map(|raw| Idx::from_raw(raw.into()))
+    }
 }
 
 impl<T> DoubleEndedIterator for IdxRange<T> {
@@ -162,6 +222,10 @@ impl<T> DoubleEndedIterator for IdxRange<T> {
     }
 }
 
+impl<T> ExactSizeIterator for IdxRange<T> {}
+
+impl<T> FusedIterator for IdxRange<T> {}
+
 impl<T> fmt::Debug for IdxRange<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_tuple(&format!("IdxRange::<{}>", std::any::type_name::<T>()))
@@ -280,6 +344,21 @@ impl<T> Arena<T> {
         idx
     }
 
+    /// Densely allocates multiple values, returning the values’ index range.
+    ///
+    /// ```
+    /// let mut arena = la_arena::Arena::new();
+    /// let range = arena.alloc_many(0..4);
+    ///
+    /// assert_eq!(arena[range], [0, 1, 2, 3]);
+    /// ```
+    pub fn alloc_many<II: IntoIterator<Item = T>>(&mut self, iter: II) -> IdxRange<T> {
+        let start = self.next_idx();
+        self.extend(iter);
+        let end = self.next_idx();
+        IdxRange::new(start..end)
+    }
+
     /// Returns an iterator over the arena’s elements.
     ///
     /// ```
@@ -295,7 +374,7 @@ impl<T> Arena<T> {
     /// ```
     pub fn iter(
         &self,
-    ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
+    ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone {
         self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
     }
 
@@ -335,7 +414,7 @@ impl<T> Arena<T> {
     /// assert_eq!(iterator.next(), Some(&40));
     /// assert_eq!(iterator.next(), Some(&60));
     /// ```
-    pub fn values(&mut self) -> impl Iterator<Item = &T> + ExactSizeIterator + DoubleEndedIterator {
+    pub fn values(&self) -> impl Iterator<Item = &T> + ExactSizeIterator + DoubleEndedIterator {
         self.data.iter()
     }
 
@@ -410,3 +489,32 @@ impl<T> FromIterator<T> for Arena<T> {
         Arena { data: Vec::from_iter(iter) }
     }
 }
+
+/// An iterator over the arena’s elements.
+pub struct IntoIter<T>(Enumerate<<Vec<T> as IntoIterator>::IntoIter>);
+
+impl<T> Iterator for IntoIter<T> {
+    type Item = (Idx<T>, T);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
+    }
+}
+
+impl<T> IntoIterator for Arena<T> {
+    type Item = (Idx<T>, T);
+
+    type IntoIter = IntoIter<T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        IntoIter(self.data.into_iter().enumerate())
+    }
+}
+
+impl<T> Extend<T> for Arena<T> {
+    fn extend<II: IntoIterator<Item = T>>(&mut self, iter: II) {
+        for t in iter {
+            self.alloc(t);
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
index 7fff2b09c97..750f345b539 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
@@ -1,3 +1,4 @@
+use std::iter::Enumerate;
 use std::marker::PhantomData;
 
 use crate::Idx;
@@ -72,17 +73,17 @@ impl<T, V> ArenaMap<Idx<T>, V> {
     }
 
     /// Returns an iterator over the values in the map.
-    pub fn values(&self) -> impl Iterator<Item = &V> {
+    pub fn values(&self) -> impl Iterator<Item = &V> + DoubleEndedIterator {
         self.v.iter().filter_map(|o| o.as_ref())
     }
 
     /// Returns an iterator over mutable references to the values in the map.
-    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
+    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> + DoubleEndedIterator {
         self.v.iter_mut().filter_map(|o| o.as_mut())
     }
 
     /// Returns an iterator over the arena indexes and values in the map.
-    pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> {
+    pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> + DoubleEndedIterator {
         self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
     }
 
@@ -94,12 +95,6 @@ impl<T, V> ArenaMap<Idx<T>, V> {
             .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
     }
 
-    /// Returns an iterator over the arena indexes and values in the map.
-    // FIXME: Implement `IntoIterator` trait.
-    pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> {
-        self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?)))
-    }
-
     /// Gets the given key's corresponding entry in the map for in-place manipulation.
     pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
         let idx = Self::to_idx(idx);
@@ -154,6 +149,63 @@ impl<T, V> FromIterator<(Idx<V>, T)> for ArenaMap<Idx<V>, T> {
     }
 }
 
+pub struct ArenaMapIter<IDX, V> {
+    iter: Enumerate<std::vec::IntoIter<Option<V>>>,
+    _ty: PhantomData<IDX>,
+}
+
+impl<T, V> IntoIterator for ArenaMap<Idx<T>, V> {
+    type Item = (Idx<T>, V);
+
+    type IntoIter = ArenaMapIter<Idx<T>, V>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        let iter = self.v.into_iter().enumerate();
+        Self::IntoIter { iter, _ty: PhantomData }
+    }
+}
+
+impl<T, V> ArenaMapIter<Idx<T>, V> {
+    fn mapper((idx, o): (usize, Option<V>)) -> Option<(Idx<T>, V)> {
+        Some((ArenaMap::<Idx<T>, V>::from_idx(idx), o?))
+    }
+}
+
+impl<T, V> Iterator for ArenaMapIter<Idx<T>, V> {
+    type Item = (Idx<T>, V);
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        for next in self.iter.by_ref() {
+            match Self::mapper(next) {
+                Some(r) => return Some(r),
+                None => continue,
+            }
+        }
+
+        None
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl<T, V> DoubleEndedIterator for ArenaMapIter<Idx<T>, V> {
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        while let Some(next_back) = self.iter.next_back() {
+            match Self::mapper(next_back) {
+                Some(r) => return Some(r),
+                None => continue,
+            }
+        }
+
+        None
+    }
+}
+
 /// A view into a single entry in a map, which may either be vacant or occupied.
 ///
 /// This `enum` is constructed from the [`entry`] method on [`ArenaMap`].
diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
new file mode 100644
index 00000000000..019ad3a53ba
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "line-index"
+version = "0.1.0-pre.1"
+description = "Maps flat `TextSize` offsets to/from `(line, column)` representation."
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index"
+edition = "2021"
+
+[dependencies]
+text-size.workspace = true
+nohash-hasher.workspace = true
diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
new file mode 100644
index 00000000000..ad67d3f246e
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
@@ -0,0 +1,237 @@
+//! See [`LineIndex`].
+
+#![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)]
+
+#[cfg(test)]
+mod tests;
+
+use nohash_hasher::IntMap;
+
+pub use text_size::{TextRange, TextSize};
+
+/// `(line, column)` information in the native, UTF-8 encoding.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct LineCol {
+    /// Zero-based.
+    pub line: u32,
+    /// Zero-based UTF-8 offset.
+    pub col: u32,
+}
+
+/// A kind of wide character encoding.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub enum WideEncoding {
+    /// UTF-16.
+    Utf16,
+    /// UTF-32.
+    Utf32,
+}
+
+impl WideEncoding {
+    /// Returns the number of code units it takes to encode `text` in this encoding.
+    pub fn measure(&self, text: &str) -> usize {
+        match self {
+            WideEncoding::Utf16 => text.encode_utf16().count(),
+            WideEncoding::Utf32 => text.chars().count(),
+        }
+    }
+}
+
+/// `(line, column)` information in wide encodings.
+///
+/// See [`WideEncoding`] for the kinds of wide encodings available.
+//
+// Deliberately not a generic type and different from `LineCol`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct WideLineCol {
+    /// Zero-based.
+    pub line: u32,
+    /// Zero-based.
+    pub col: u32,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct WideChar {
+    /// Start offset of a character inside a line, zero-based.
+    start: TextSize,
+    /// End offset of a character inside a line, zero-based.
+    end: TextSize,
+}
+
+impl WideChar {
+    /// Returns the length in 8-bit UTF-8 code units.
+    fn len(&self) -> TextSize {
+        self.end - self.start
+    }
+
+    /// Returns the length in UTF-16 or UTF-32 code units.
+    fn wide_len(&self, enc: WideEncoding) -> u32 {
+        match enc {
+            WideEncoding::Utf16 => {
+                if self.len() == TextSize::from(4) {
+                    2
+                } else {
+                    1
+                }
+            }
+            WideEncoding::Utf32 => 1,
+        }
+    }
+}
+
+/// Maps flat [`TextSize`] offsets to/from `(line, column)` representation.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LineIndex {
+    /// Offset the beginning of each line (except the first, which always has offset 0).
+    newlines: Box<[TextSize]>,
+    /// List of non-ASCII characters on each line.
+    line_wide_chars: IntMap<u32, Box<[WideChar]>>,
+    /// The length of the entire text.
+    len: TextSize,
+}
+
+impl LineIndex {
+    /// Returns a `LineIndex` for the `text`.
+    pub fn new(text: &str) -> LineIndex {
+        let mut newlines = Vec::<TextSize>::with_capacity(16);
+        let mut line_wide_chars = IntMap::<u32, Box<[WideChar]>>::default();
+
+        let mut wide_chars = Vec::<WideChar>::new();
+        let mut cur_row = TextSize::from(0);
+        let mut cur_col = TextSize::from(0);
+        let mut line = 0u32;
+
+        for c in text.chars() {
+            let c_len = TextSize::of(c);
+            cur_row += c_len;
+            if c == '\n' {
+                newlines.push(cur_row);
+
+                // Save any wide characters seen in the previous line
+                if !wide_chars.is_empty() {
+                    let cs = std::mem::take(&mut wide_chars).into_boxed_slice();
+                    line_wide_chars.insert(line, cs);
+                }
+
+                // Prepare for processing the next line
+                cur_col = TextSize::from(0);
+                line += 1;
+                continue;
+            }
+
+            if !c.is_ascii() {
+                wide_chars.push(WideChar { start: cur_col, end: cur_col + c_len });
+            }
+
+            cur_col += c_len;
+        }
+
+        // Save any wide characters seen in the last line
+        if !wide_chars.is_empty() {
+            line_wide_chars.insert(line, wide_chars.into_boxed_slice());
+        }
+
+        LineIndex {
+            newlines: newlines.into_boxed_slice(),
+            line_wide_chars,
+            len: TextSize::of(text),
+        }
+    }
+
+    /// Transforms the `TextSize` into a `LineCol`.
+    ///
+    /// # Panics
+    ///
+    /// If the offset is invalid. See [`Self::try_line_col`].
+    pub fn line_col(&self, offset: TextSize) -> LineCol {
+        self.try_line_col(offset).expect("invalid offset")
+    }
+
+    /// Transforms the `TextSize` into a `LineCol`.
+    ///
+    /// Returns `None` if the `offset` was invalid, e.g. if it extends past the end of the text or
+    /// points to the middle of a multi-byte character.
+    pub fn try_line_col(&self, offset: TextSize) -> Option<LineCol> {
+        if offset > self.len {
+            return None;
+        }
+        let line = self.newlines.partition_point(|&it| it <= offset);
+        let start = self.start_offset(line)?;
+        let col = offset - start;
+        let ret = LineCol { line: line as u32, col: col.into() };
+        self.line_wide_chars
+            .get(&ret.line)
+            .into_iter()
+            .flat_map(|it| it.iter())
+            .all(|it| col <= it.start || it.end <= col)
+            .then_some(ret)
+    }
+
+    /// Transforms the `LineCol` into a `TextSize`.
+    pub fn offset(&self, line_col: LineCol) -> Option<TextSize> {
+        self.start_offset(line_col.line as usize).map(|start| start + TextSize::from(line_col.col))
+    }
+
+    fn start_offset(&self, line: usize) -> Option<TextSize> {
+        match line.checked_sub(1) {
+            None => Some(TextSize::from(0)),
+            Some(it) => self.newlines.get(it).copied(),
+        }
+    }
+
+    /// Transforms the `LineCol` with the given `WideEncoding` into a `WideLineCol`.
+    pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> Option<WideLineCol> {
+        let mut col = line_col.col;
+        if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) {
+            for c in wide_chars.iter() {
+                if u32::from(c.end) <= line_col.col {
+                    col = col.checked_sub(u32::from(c.len()) - c.wide_len(enc))?;
+                } else {
+                    // From here on, all utf16 characters come *after* the character we are mapping,
+                    // so we don't need to take them into account
+                    break;
+                }
+            }
+        }
+        Some(WideLineCol { line: line_col.line, col })
+    }
+
+    /// Transforms the `WideLineCol` with the given `WideEncoding` into a `LineCol`.
+    pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> Option<LineCol> {
+        let mut col = line_col.col;
+        if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) {
+            for c in wide_chars.iter() {
+                if col > u32::from(c.start) {
+                    col = col.checked_add(u32::from(c.len()) - c.wide_len(enc))?;
+                } else {
+                    // From here on, all utf16 characters come *after* the character we are mapping,
+                    // so we don't need to take them into account
+                    break;
+                }
+            }
+        }
+        Some(LineCol { line: line_col.line, col })
+    }
+
+    /// Given a range [start, end), returns a sorted iterator of non-empty ranges [start, x1), [x1,
+    /// x2), ..., [xn, end) where all the xi, which are positions of newlines, are inside the range
+    /// [start, end).
+    pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
+        let lo = self.newlines.partition_point(|&it| it < range.start());
+        let hi = self.newlines.partition_point(|&it| it <= range.end());
+        let all = std::iter::once(range.start())
+            .chain(self.newlines[lo..hi].iter().copied())
+            .chain(std::iter::once(range.end()));
+
+        all.clone()
+            .zip(all.skip(1))
+            .map(|(lo, hi)| TextRange::new(lo, hi))
+            .filter(|it| !it.is_empty())
+    }
+
+    /// Returns the length of the original text.
+    pub fn len(&self) -> TextSize {
+        self.len
+    }
+}
diff --git a/src/tools/rust-analyzer/lib/line-index/src/tests.rs b/src/tools/rust-analyzer/lib/line-index/src/tests.rs
new file mode 100644
index 00000000000..31c01c20ee3
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/src/tests.rs
@@ -0,0 +1,11 @@
+use super::LineIndex;
+
+#[test]
+fn test_empty_index() {
+    let col_index = LineIndex::new(
+        "
+const C: char = 'x';
+",
+    );
+    assert_eq!(col_index.line_wide_chars.len(), 0);
+}
diff --git a/src/tools/rust-analyzer/lib/line-index/tests/it.rs b/src/tools/rust-analyzer/lib/line-index/tests/it.rs
new file mode 100644
index 00000000000..ce1c0bc6f14
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/line-index/tests/it.rs
@@ -0,0 +1,62 @@
+use line_index::{LineCol, LineIndex, TextRange};
+
+#[test]
+fn test_line_index() {
+    let text = "hello\nworld";
+    let table = [
+        (00, 0, 0),
+        (01, 0, 1),
+        (05, 0, 5),
+        (06, 1, 0),
+        (07, 1, 1),
+        (08, 1, 2),
+        (10, 1, 4),
+        (11, 1, 5),
+    ];
+
+    let index = LineIndex::new(text);
+    for (offset, line, col) in table {
+        assert_eq!(index.line_col(offset.into()), LineCol { line, col });
+    }
+
+    let text = "\nhello\nworld";
+    let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
+    let index = LineIndex::new(text);
+    for (offset, line, col) in table {
+        assert_eq!(index.line_col(offset.into()), LineCol { line, col });
+    }
+}
+
+#[test]
+fn test_char_len() {
+    assert_eq!('メ'.len_utf8(), 3);
+    assert_eq!('メ'.len_utf16(), 1);
+}
+
+#[test]
+fn test_splitlines() {
+    fn r(lo: u32, hi: u32) -> TextRange {
+        TextRange::new(lo.into(), hi.into())
+    }
+
+    let text = "a\nbb\nccc\n";
+    let line_index = LineIndex::new(text);
+
+    let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
+    let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
+    assert_eq!(actual, expected);
+
+    let text = "";
+    let line_index = LineIndex::new(text);
+
+    let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
+    let expected = vec![];
+    assert_eq!(actual, expected);
+
+    let text = "\n";
+    let line_index = LineIndex::new(text);
+
+    let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
+    let expected = vec![r(0, 1)];
+    assert_eq!(actual, expected)
+}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index 6e32e396052..e78a9d2eb16 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -8,8 +8,8 @@ edition = "2021"
 
 [dependencies]
 log = "0.4.17"
-serde_json = "1.0.86"
-serde = { version = "1.0.144", features = ["derive"] }
+serde_json.workspace = true
+serde.workspace = true
 crossbeam-channel = "0.5.6"
 
 [dev-dependencies]
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
index 4c934d9ecca..755b3fd9596 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs
@@ -2,7 +2,7 @@ use std::fmt;
 
 use crate::{Notification, Request};
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct ProtocolError(pub(crate) String);
 
 impl std::error::Error for ProtocolError {}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
index beccde40a89..affab60a227 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
@@ -126,6 +126,9 @@ impl Connection {
                     self.sender.send(resp.into()).unwrap();
                     continue;
                 }
+                Ok(Message::Notification(n)) if !n.is_exit() => {
+                    continue;
+                }
                 Ok(msg) => Err(ProtocolError(format!("expected initialize request, got {msg:?}"))),
                 Err(e) => {
                     Err(ProtocolError(format!("expected initialize request, got error: {e}")))
@@ -212,3 +215,70 @@ impl Connection {
         Ok(true)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use crossbeam_channel::unbounded;
+    use lsp_types::notification::{Exit, Initialized, Notification};
+    use lsp_types::request::{Initialize, Request};
+    use lsp_types::{InitializeParams, InitializedParams};
+    use serde_json::to_value;
+
+    use crate::{Connection, Message, ProtocolError, RequestId};
+
+    struct TestCase {
+        test_messages: Vec<Message>,
+        expected_resp: Result<(RequestId, serde_json::Value), ProtocolError>,
+    }
+
+    fn initialize_start_test(test_case: TestCase) {
+        let (reader_sender, reader_receiver) = unbounded::<Message>();
+        let (writer_sender, writer_receiver) = unbounded::<Message>();
+        let conn = Connection { sender: writer_sender, receiver: reader_receiver };
+
+        for msg in test_case.test_messages {
+            assert!(reader_sender.send(msg).is_ok());
+        }
+
+        let resp = conn.initialize_start();
+        assert_eq!(test_case.expected_resp, resp);
+
+        assert!(writer_receiver.recv_timeout(std::time::Duration::from_secs(1)).is_err());
+    }
+
+    #[test]
+    fn not_exit_notification() {
+        let notification = crate::Notification {
+            method: Initialized::METHOD.to_string(),
+            params: to_value(InitializedParams {}).unwrap(),
+        };
+
+        let params_as_value = to_value(InitializeParams::default()).unwrap();
+        let req_id = RequestId::from(234);
+        let request = crate::Request {
+            id: req_id.clone(),
+            method: Initialize::METHOD.to_string(),
+            params: params_as_value.clone(),
+        };
+
+        initialize_start_test(TestCase {
+            test_messages: vec![notification.into(), request.into()],
+            expected_resp: Ok((req_id, params_as_value)),
+        });
+    }
+
+    #[test]
+    fn exit_notification() {
+        let notification =
+            crate::Notification { method: Exit::METHOD.to_string(), params: to_value(()).unwrap() };
+        let notification_msg = Message::from(notification);
+
+        initialize_start_test(TestCase {
+            test_messages: vec![notification_msg.clone()],
+            expected_resp: Err(ProtocolError(format!(
+                "expected initialize request, got {:?}",
+                notification_msg
+            ))),
+        });
+    }
+}