Checkpoint

Antonio Scandurra created

Change summary

Cargo.lock                             |   53 
Cargo.toml                             |    3 
crates/fuzzy2/Cargo.toml               |   13 
crates/fuzzy2/src/char_bag.rs          |   63 
crates/fuzzy2/src/fuzzy2.rs            |   10 
crates/fuzzy2/src/matcher.rs           |  464 ++
crates/fuzzy2/src/paths.rs             |  257 +
crates/fuzzy2/src/strings.rs           |  159 
crates/gpui2/src/executor.rs           |   14 
crates/language2/Cargo.toml            |   10 
crates/language2/src/buffer.rs         |   13 
crates/language2/src/buffer_tests.rs   | 4884 ++++++++++++++--------------
crates/language2/src/diagnostic_set.rs |    8 
crates/language2/src/highlight_map.rs  |   62 
crates/language2/src/language2.rs      |   83 
crates/language2/src/outline.rs        |    6 
crates/language2/src/proto.rs          |    2 
crates/lsp2/Cargo.toml                 |   38 
crates/lsp2/src/lsp2.rs                | 1167 ++++++
crates/theme2/Cargo.toml               |   35 
crates/theme2/src/theme2.rs            |   21 
crates/welcome/Cargo.toml              |    2 
crates/zed2/src/main.rs                |    2 
23 files changed, 4,833 insertions(+), 2,536 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3129,6 +3129,14 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "fuzzy2"
+version = "0.1.0"
+dependencies = [
+ "gpui2",
+ "util",
+]
+
 [[package]]
 name = "fxhash"
 version = "0.2.1"
@@ -4174,14 +4182,14 @@ dependencies = [
  "env_logger 0.9.3",
  "fs",
  "futures 0.3.28",
- "fuzzy",
+ "fuzzy2",
  "git",
  "globset",
  "gpui2",
  "indoc",
  "lazy_static",
  "log",
- "lsp",
+ "lsp2",
  "parking_lot 0.11.2",
  "postage",
  "rand 0.8.5",
@@ -4198,6 +4206,7 @@ dependencies = [
  "smol",
  "sum_tree",
  "text",
+ "theme2",
  "tree-sitter",
  "tree-sitter-elixir",
  "tree-sitter-embedded-template",
@@ -4492,6 +4501,29 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "lsp2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-pipe",
+ "collections",
+ "ctor",
+ "env_logger 0.9.3",
+ "futures 0.3.28",
+ "gpui2",
+ "log",
+ "lsp-types",
+ "parking_lot 0.11.2",
+ "postage",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smol",
+ "unindent",
+ "util",
+]
+
 [[package]]
 name = "mach"
 version = "0.3.2"
@@ -8343,6 +8375,23 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "theme2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "fs",
+ "gpui2",
+ "indexmap 1.9.3",
+ "parking_lot 0.11.2",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "settings2",
+ "toml 0.5.11",
+ "util",
+]
+
 [[package]]
 name = "theme_selector"
 version = "0.1.0"

Cargo.toml 🔗

@@ -34,6 +34,7 @@ members = [
     "crates/fs",
     "crates/fsevent",
     "crates/fuzzy",
+    "crates/fuzzy2",
     "crates/git",
     "crates/go_to_line",
     "crates/gpui",
@@ -49,6 +50,7 @@ members = [
     "crates/live_kit_client",
     "crates/live_kit_server",
     "crates/lsp",
+    "crates/lsp2",
     "crates/media",
     "crates/menu",
     "crates/node_runtime",
@@ -76,6 +78,7 @@ members = [
     "crates/terminal",
     "crates/text",
     "crates/theme",
+    "crates/theme2",
     "crates/theme_selector",
     "crates/ui2",
     "crates/util",

crates/fuzzy2/Cargo.toml 🔗

@@ -0,0 +1,13 @@
+[package]
+name = "fuzzy2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/fuzzy2.rs"
+doctest = false
+
+[dependencies]
+gpui2 = { path = "../gpui2" }
+util = { path = "../util" }

crates/fuzzy2/src/char_bag.rs 🔗

@@ -0,0 +1,63 @@
+use std::iter::FromIterator;
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub struct CharBag(u64);
+
+impl CharBag {
+    pub fn is_superset(self, other: CharBag) -> bool {
+        self.0 & other.0 == other.0
+    }
+
+    fn insert(&mut self, c: char) {
+        let c = c.to_ascii_lowercase();
+        if ('a'..='z').contains(&c) {
+            let mut count = self.0;
+            let idx = c as u8 - b'a';
+            count >>= idx * 2;
+            count = ((count << 1) | 1) & 3;
+            count <<= idx * 2;
+            self.0 |= count;
+        } else if ('0'..='9').contains(&c) {
+            let idx = c as u8 - b'0';
+            self.0 |= 1 << (idx + 52);
+        } else if c == '-' {
+            self.0 |= 1 << 62;
+        }
+    }
+}
+
+impl Extend<char> for CharBag {
+    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
+        for c in iter {
+            self.insert(c);
+        }
+    }
+}
+
+impl FromIterator<char> for CharBag {
+    fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
+        let mut result = Self::default();
+        result.extend(iter);
+        result
+    }
+}
+
+impl From<&str> for CharBag {
+    fn from(s: &str) -> Self {
+        let mut bag = Self(0);
+        for c in s.chars() {
+            bag.insert(c);
+        }
+        bag
+    }
+}
+
+impl From<&[char]> for CharBag {
+    fn from(chars: &[char]) -> Self {
+        let mut bag = Self(0);
+        for c in chars {
+            bag.insert(*c);
+        }
+        bag
+    }
+}

crates/fuzzy2/src/fuzzy2.rs 🔗

@@ -0,0 +1,10 @@
+mod char_bag;
+mod matcher;
+mod paths;
+mod strings;
+
+pub use char_bag::CharBag;
+pub use paths::{
+    match_fixed_path_set, match_path_sets, PathMatch, PathMatchCandidate, PathMatchCandidateSet,
+};
+pub use strings::{match_strings, StringMatch, StringMatchCandidate};

crates/fuzzy2/src/matcher.rs 🔗

@@ -0,0 +1,464 @@
+use std::{
+    borrow::Cow,
+    sync::atomic::{self, AtomicBool},
+};
+
+use crate::CharBag;
+
+const BASE_DISTANCE_PENALTY: f64 = 0.6;
+const ADDITIONAL_DISTANCE_PENALTY: f64 = 0.05;
+const MIN_DISTANCE_PENALTY: f64 = 0.2;
+
+pub struct Matcher<'a> {
+    query: &'a [char],
+    lowercase_query: &'a [char],
+    query_char_bag: CharBag,
+    smart_case: bool,
+    max_results: usize,
+    min_score: f64,
+    match_positions: Vec<usize>,
+    last_positions: Vec<usize>,
+    score_matrix: Vec<Option<f64>>,
+    best_position_matrix: Vec<usize>,
+}
+
+pub trait Match: Ord {
+    fn score(&self) -> f64;
+    fn set_positions(&mut self, positions: Vec<usize>);
+}
+
+pub trait MatchCandidate {
+    fn has_chars(&self, bag: CharBag) -> bool;
+    fn to_string(&self) -> Cow<'_, str>;
+}
+
+impl<'a> Matcher<'a> {
+    pub fn new(
+        query: &'a [char],
+        lowercase_query: &'a [char],
+        query_char_bag: CharBag,
+        smart_case: bool,
+        max_results: usize,
+    ) -> Self {
+        Self {
+            query,
+            lowercase_query,
+            query_char_bag,
+            min_score: 0.0,
+            last_positions: vec![0; query.len()],
+            match_positions: vec![0; query.len()],
+            score_matrix: Vec::new(),
+            best_position_matrix: Vec::new(),
+            smart_case,
+            max_results,
+        }
+    }
+
+    pub fn match_candidates<C: MatchCandidate, R, F>(
+        &mut self,
+        prefix: &[char],
+        lowercase_prefix: &[char],
+        candidates: impl Iterator<Item = C>,
+        results: &mut Vec<R>,
+        cancel_flag: &AtomicBool,
+        build_match: F,
+    ) where
+        R: Match,
+        F: Fn(&C, f64) -> R,
+    {
+        let mut candidate_chars = Vec::new();
+        let mut lowercase_candidate_chars = Vec::new();
+
+        for candidate in candidates {
+            if !candidate.has_chars(self.query_char_bag) {
+                continue;
+            }
+
+            if cancel_flag.load(atomic::Ordering::Relaxed) {
+                break;
+            }
+
+            candidate_chars.clear();
+            lowercase_candidate_chars.clear();
+            for c in candidate.to_string().chars() {
+                candidate_chars.push(c);
+                lowercase_candidate_chars.push(c.to_ascii_lowercase());
+            }
+
+            if !self.find_last_positions(lowercase_prefix, &lowercase_candidate_chars) {
+                continue;
+            }
+
+            let matrix_len = self.query.len() * (prefix.len() + candidate_chars.len());
+            self.score_matrix.clear();
+            self.score_matrix.resize(matrix_len, None);
+            self.best_position_matrix.clear();
+            self.best_position_matrix.resize(matrix_len, 0);
+
+            let score = self.score_match(
+                &candidate_chars,
+                &lowercase_candidate_chars,
+                prefix,
+                lowercase_prefix,
+            );
+
+            if score > 0.0 {
+                let mut mat = build_match(&candidate, score);
+                if let Err(i) = results.binary_search_by(|m| mat.cmp(m)) {
+                    if results.len() < self.max_results {
+                        mat.set_positions(self.match_positions.clone());
+                        results.insert(i, mat);
+                    } else if i < results.len() {
+                        results.pop();
+                        mat.set_positions(self.match_positions.clone());
+                        results.insert(i, mat);
+                    }
+                    if results.len() == self.max_results {
+                        self.min_score = results.last().unwrap().score();
+                    }
+                }
+            }
+        }
+    }
+
+    fn find_last_positions(
+        &mut self,
+        lowercase_prefix: &[char],
+        lowercase_candidate: &[char],
+    ) -> bool {
+        let mut lowercase_prefix = lowercase_prefix.iter();
+        let mut lowercase_candidate = lowercase_candidate.iter();
+        for (i, char) in self.lowercase_query.iter().enumerate().rev() {
+            if let Some(j) = lowercase_candidate.rposition(|c| c == char) {
+                self.last_positions[i] = j + lowercase_prefix.len();
+            } else if let Some(j) = lowercase_prefix.rposition(|c| c == char) {
+                self.last_positions[i] = j;
+            } else {
+                return false;
+            }
+        }
+        true
+    }
+
+    fn score_match(
+        &mut self,
+        path: &[char],
+        path_cased: &[char],
+        prefix: &[char],
+        lowercase_prefix: &[char],
+    ) -> f64 {
+        let score = self.recursive_score_match(
+            path,
+            path_cased,
+            prefix,
+            lowercase_prefix,
+            0,
+            0,
+            self.query.len() as f64,
+        ) * self.query.len() as f64;
+
+        if score <= 0.0 {
+            return 0.0;
+        }
+
+        let path_len = prefix.len() + path.len();
+        let mut cur_start = 0;
+        let mut byte_ix = 0;
+        let mut char_ix = 0;
+        for i in 0..self.query.len() {
+            let match_char_ix = self.best_position_matrix[i * path_len + cur_start];
+            while char_ix < match_char_ix {
+                let ch = prefix
+                    .get(char_ix)
+                    .or_else(|| path.get(char_ix - prefix.len()))
+                    .unwrap();
+                byte_ix += ch.len_utf8();
+                char_ix += 1;
+            }
+            cur_start = match_char_ix + 1;
+            self.match_positions[i] = byte_ix;
+        }
+
+        score
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn recursive_score_match(
+        &mut self,
+        path: &[char],
+        path_cased: &[char],
+        prefix: &[char],
+        lowercase_prefix: &[char],
+        query_idx: usize,
+        path_idx: usize,
+        cur_score: f64,
+    ) -> f64 {
+        if query_idx == self.query.len() {
+            return 1.0;
+        }
+
+        let path_len = prefix.len() + path.len();
+
+        if let Some(memoized) = self.score_matrix[query_idx * path_len + path_idx] {
+            return memoized;
+        }
+
+        let mut score = 0.0;
+        let mut best_position = 0;
+
+        let query_char = self.lowercase_query[query_idx];
+        let limit = self.last_positions[query_idx];
+
+        let mut last_slash = 0;
+        for j in path_idx..=limit {
+            let path_char = if j < prefix.len() {
+                lowercase_prefix[j]
+            } else {
+                path_cased[j - prefix.len()]
+            };
+            let is_path_sep = path_char == '/' || path_char == '\\';
+
+            if query_idx == 0 && is_path_sep {
+                last_slash = j;
+            }
+
+            if query_char == path_char || (is_path_sep && query_char == '_' || query_char == '\\') {
+                let curr = if j < prefix.len() {
+                    prefix[j]
+                } else {
+                    path[j - prefix.len()]
+                };
+
+                let mut char_score = 1.0;
+                if j > path_idx {
+                    let last = if j - 1 < prefix.len() {
+                        prefix[j - 1]
+                    } else {
+                        path[j - 1 - prefix.len()]
+                    };
+
+                    if last == '/' {
+                        char_score = 0.9;
+                    } else if (last == '-' || last == '_' || last == ' ' || last.is_numeric())
+                        || (last.is_lowercase() && curr.is_uppercase())
+                    {
+                        char_score = 0.8;
+                    } else if last == '.' {
+                        char_score = 0.7;
+                    } else if query_idx == 0 {
+                        char_score = BASE_DISTANCE_PENALTY;
+                    } else {
+                        char_score = MIN_DISTANCE_PENALTY.max(
+                            BASE_DISTANCE_PENALTY
+                                - (j - path_idx - 1) as f64 * ADDITIONAL_DISTANCE_PENALTY,
+                        );
+                    }
+                }
+
+                // Apply a severe penalty if the case doesn't match.
+                // This will make the exact matches have higher score than the case-insensitive and the
+                // path insensitive matches.
+                if (self.smart_case || curr == '/') && self.query[query_idx] != curr {
+                    char_score *= 0.001;
+                }
+
+                let mut multiplier = char_score;
+
+                // Scale the score based on how deep within the path we found the match.
+                if query_idx == 0 {
+                    multiplier /= ((prefix.len() + path.len()) - last_slash) as f64;
+                }
+
+                let mut next_score = 1.0;
+                if self.min_score > 0.0 {
+                    next_score = cur_score * multiplier;
+                    // Scores only decrease. If we can't pass the previous best, bail
+                    if next_score < self.min_score {
+                        // Ensure that score is non-zero so we use it in the memo table.
+                        if score == 0.0 {
+                            score = 1e-18;
+                        }
+                        continue;
+                    }
+                }
+
+                let new_score = self.recursive_score_match(
+                    path,
+                    path_cased,
+                    prefix,
+                    lowercase_prefix,
+                    query_idx + 1,
+                    j + 1,
+                    next_score,
+                ) * multiplier;
+
+                if new_score > score {
+                    score = new_score;
+                    best_position = j;
+                    // Optimization: can't score better than 1.
+                    if new_score == 1.0 {
+                        break;
+                    }
+                }
+            }
+        }
+
+        if best_position != 0 {
+            self.best_position_matrix[query_idx * path_len + path_idx] = best_position;
+        }
+
+        self.score_matrix[query_idx * path_len + path_idx] = Some(score);
+        score
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{PathMatch, PathMatchCandidate};
+
+    use super::*;
+    use std::{
+        path::{Path, PathBuf},
+        sync::Arc,
+    };
+
+    #[test]
+    fn test_get_last_positions() {
+        let mut query: &[char] = &['d', 'c'];
+        let mut matcher = Matcher::new(query, query, query.into(), false, 10);
+        let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']);
+        assert!(!result);
+
+        query = &['c', 'd'];
+        let mut matcher = Matcher::new(query, query, query.into(), false, 10);
+        let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']);
+        assert!(result);
+        assert_eq!(matcher.last_positions, vec![2, 4]);
+
+        query = &['z', '/', 'z', 'f'];
+        let mut matcher = Matcher::new(query, query, query.into(), false, 10);
+        let result = matcher.find_last_positions(&['z', 'e', 'd', '/'], &['z', 'e', 'd', '/', 'f']);
+        assert!(result);
+        assert_eq!(matcher.last_positions, vec![0, 3, 4, 8]);
+    }
+
+    #[test]
+    fn test_match_path_entries() {
+        let paths = vec![
+            "",
+            "a",
+            "ab",
+            "abC",
+            "abcd",
+            "alphabravocharlie",
+            "AlphaBravoCharlie",
+            "thisisatestdir",
+            "/////ThisIsATestDir",
+            "/this/is/a/test/dir",
+            "/test/tiatd",
+        ];
+
+        assert_eq!(
+            match_single_path_query("abc", false, &paths),
+            vec![
+                ("abC", vec![0, 1, 2]),
+                ("abcd", vec![0, 1, 2]),
+                ("AlphaBravoCharlie", vec![0, 5, 10]),
+                ("alphabravocharlie", vec![4, 5, 10]),
+            ]
+        );
+        assert_eq!(
+            match_single_path_query("t/i/a/t/d", false, &paths),
+            vec![("/this/is/a/test/dir", vec![1, 5, 6, 8, 9, 10, 11, 15, 16]),]
+        );
+
+        assert_eq!(
+            match_single_path_query("tiatd", false, &paths),
+            vec![
+                ("/test/tiatd", vec![6, 7, 8, 9, 10]),
+                ("/this/is/a/test/dir", vec![1, 6, 9, 11, 16]),
+                ("/////ThisIsATestDir", vec![5, 9, 11, 12, 16]),
+                ("thisisatestdir", vec![0, 2, 6, 7, 11]),
+            ]
+        );
+    }
+
+    #[test]
+    fn test_match_multibyte_path_entries() {
+        let paths = vec!["aαbβ/cγdδ", "αβγδ/bcde", "c1️⃣2️⃣3️⃣/d4️⃣5️⃣6️⃣/e7️⃣8️⃣9️⃣/f", "/d/🆒/h"];
+        assert_eq!("1️⃣".len(), 7);
+        assert_eq!(
+            match_single_path_query("bcd", false, &paths),
+            vec![
+                ("αβγδ/bcde", vec![9, 10, 11]),
+                ("aαbβ/cγdδ", vec![3, 7, 10]),
+            ]
+        );
+        assert_eq!(
+            match_single_path_query("cde", false, &paths),
+            vec![
+                ("αβγδ/bcde", vec![10, 11, 12]),
+                ("c1️⃣2️⃣3️⃣/d4️⃣5️⃣6️⃣/e7️⃣8️⃣9️⃣/f", vec![0, 23, 46]),
+            ]
+        );
+    }
+
+    fn match_single_path_query<'a>(
+        query: &str,
+        smart_case: bool,
+        paths: &[&'a str],
+    ) -> Vec<(&'a str, Vec<usize>)> {
+        let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
+        let query = query.chars().collect::<Vec<_>>();
+        let query_chars = CharBag::from(&lowercase_query[..]);
+
+        let path_arcs: Vec<Arc<Path>> = paths
+            .iter()
+            .map(|path| Arc::from(PathBuf::from(path)))
+            .collect::<Vec<_>>();
+        let mut path_entries = Vec::new();
+        for (i, path) in paths.iter().enumerate() {
+            let lowercase_path = path.to_lowercase().chars().collect::<Vec<_>>();
+            let char_bag = CharBag::from(lowercase_path.as_slice());
+            path_entries.push(PathMatchCandidate {
+                char_bag,
+                path: &path_arcs[i],
+            });
+        }
+
+        let mut matcher = Matcher::new(&query, &lowercase_query, query_chars, smart_case, 100);
+
+        let cancel_flag = AtomicBool::new(false);
+        let mut results = Vec::new();
+
+        matcher.match_candidates(
+            &[],
+            &[],
+            path_entries.into_iter(),
+            &mut results,
+            &cancel_flag,
+            |candidate, score| PathMatch {
+                score,
+                worktree_id: 0,
+                positions: Vec::new(),
+                path: Arc::from(candidate.path),
+                path_prefix: "".into(),
+                distance_to_relative_ancestor: usize::MAX,
+            },
+        );
+
+        results
+            .into_iter()
+            .map(|result| {
+                (
+                    paths
+                        .iter()
+                        .copied()
+                        .find(|p| result.path.as_ref() == Path::new(p))
+                        .unwrap(),
+                    result.positions,
+                )
+            })
+            .collect()
+    }
+}

crates/fuzzy2/src/paths.rs 🔗

@@ -0,0 +1,257 @@
+use gpui2::Executor;
+use std::{
+    borrow::Cow,
+    cmp::{self, Ordering},
+    path::Path,
+    sync::{atomic::AtomicBool, Arc},
+};
+
+use crate::{
+    matcher::{Match, MatchCandidate, Matcher},
+    CharBag,
+};
+
+#[derive(Clone, Debug)]
+pub struct PathMatchCandidate<'a> {
+    pub path: &'a Path,
+    pub char_bag: CharBag,
+}
+
+#[derive(Clone, Debug)]
+pub struct PathMatch {
+    pub score: f64,
+    pub positions: Vec<usize>,
+    pub worktree_id: usize,
+    pub path: Arc<Path>,
+    pub path_prefix: Arc<str>,
+    /// Number of steps removed from a shared parent with the relative path
+    /// Used to order closer paths first in the search list
+    pub distance_to_relative_ancestor: usize,
+}
+
+pub trait PathMatchCandidateSet<'a>: Send + Sync {
+    type Candidates: Iterator<Item = PathMatchCandidate<'a>>;
+    fn id(&self) -> usize;
+    fn len(&self) -> usize;
+    fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+    fn prefix(&self) -> Arc<str>;
+    fn candidates(&'a self, start: usize) -> Self::Candidates;
+}
+
+impl Match for PathMatch {
+    fn score(&self) -> f64 {
+        self.score
+    }
+
+    fn set_positions(&mut self, positions: Vec<usize>) {
+        self.positions = positions;
+    }
+}
+
+impl<'a> MatchCandidate for PathMatchCandidate<'a> {
+    fn has_chars(&self, bag: CharBag) -> bool {
+        self.char_bag.is_superset(bag)
+    }
+
+    fn to_string(&self) -> Cow<'a, str> {
+        self.path.to_string_lossy()
+    }
+}
+
+impl PartialEq for PathMatch {
+    fn eq(&self, other: &Self) -> bool {
+        self.cmp(other).is_eq()
+    }
+}
+
+impl Eq for PathMatch {}
+
+impl PartialOrd for PathMatch {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for PathMatch {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.score
+            .partial_cmp(&other.score)
+            .unwrap_or(Ordering::Equal)
+            .then_with(|| self.worktree_id.cmp(&other.worktree_id))
+            .then_with(|| {
+                other
+                    .distance_to_relative_ancestor
+                    .cmp(&self.distance_to_relative_ancestor)
+            })
+            .then_with(|| self.path.cmp(&other.path))
+    }
+}
+
+pub fn match_fixed_path_set(
+    candidates: Vec<PathMatchCandidate>,
+    worktree_id: usize,
+    query: &str,
+    smart_case: bool,
+    max_results: usize,
+) -> Vec<PathMatch> {
+    let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
+    let query = query.chars().collect::<Vec<_>>();
+    let query_char_bag = CharBag::from(&lowercase_query[..]);
+
+    let mut matcher = Matcher::new(
+        &query,
+        &lowercase_query,
+        query_char_bag,
+        smart_case,
+        max_results,
+    );
+
+    let mut results = Vec::new();
+    matcher.match_candidates(
+        &[],
+        &[],
+        candidates.into_iter(),
+        &mut results,
+        &AtomicBool::new(false),
+        |candidate, score| PathMatch {
+            score,
+            worktree_id,
+            positions: Vec::new(),
+            path: Arc::from(candidate.path),
+            path_prefix: Arc::from(""),
+            distance_to_relative_ancestor: usize::MAX,
+        },
+    );
+    results
+}
+
+pub async fn match_path_sets<'a, Set: PathMatchCandidateSet<'a>>(
+    candidate_sets: &'a [Set],
+    query: &str,
+    relative_to: Option<Arc<Path>>,
+    smart_case: bool,
+    max_results: usize,
+    cancel_flag: &AtomicBool,
+    executor: Executor,
+) -> Vec<PathMatch> {
+    let path_count: usize = candidate_sets.iter().map(|s| s.len()).sum();
+    if path_count == 0 {
+        return Vec::new();
+    }
+
+    let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
+    let query = query.chars().collect::<Vec<_>>();
+
+    let lowercase_query = &lowercase_query;
+    let query = &query;
+    let query_char_bag = CharBag::from(&lowercase_query[..]);
+
+    let num_cpus = executor.num_cpus().min(path_count);
+    let segment_size = (path_count + num_cpus - 1) / num_cpus;
+    let mut segment_results = (0..num_cpus)
+        .map(|_| Vec::with_capacity(max_results))
+        .collect::<Vec<_>>();
+
+    executor
+        .scoped(|scope| {
+            for (segment_idx, results) in segment_results.iter_mut().enumerate() {
+                let relative_to = relative_to.clone();
+                scope.spawn(async move {
+                    let segment_start = segment_idx * segment_size;
+                    let segment_end = segment_start + segment_size;
+                    let mut matcher = Matcher::new(
+                        query,
+                        lowercase_query,
+                        query_char_bag,
+                        smart_case,
+                        max_results,
+                    );
+
+                    let mut tree_start = 0;
+                    for candidate_set in candidate_sets {
+                        let tree_end = tree_start + candidate_set.len();
+
+                        if tree_start < segment_end && segment_start < tree_end {
+                            let start = cmp::max(tree_start, segment_start) - tree_start;
+                            let end = cmp::min(tree_end, segment_end) - tree_start;
+                            let candidates = candidate_set.candidates(start).take(end - start);
+
+                            let worktree_id = candidate_set.id();
+                            let prefix = candidate_set.prefix().chars().collect::<Vec<_>>();
+                            let lowercase_prefix = prefix
+                                .iter()
+                                .map(|c| c.to_ascii_lowercase())
+                                .collect::<Vec<_>>();
+                            matcher.match_candidates(
+                                &prefix,
+                                &lowercase_prefix,
+                                candidates,
+                                results,
+                                cancel_flag,
+                                |candidate, score| PathMatch {
+                                    score,
+                                    worktree_id,
+                                    positions: Vec::new(),
+                                    path: Arc::from(candidate.path),
+                                    path_prefix: candidate_set.prefix(),
+                                    distance_to_relative_ancestor: relative_to.as_ref().map_or(
+                                        usize::MAX,
+                                        |relative_to| {
+                                            distance_between_paths(
+                                                candidate.path.as_ref(),
+                                                relative_to.as_ref(),
+                                            )
+                                        },
+                                    ),
+                                },
+                            );
+                        }
+                        if tree_end >= segment_end {
+                            break;
+                        }
+                        tree_start = tree_end;
+                    }
+                })
+            }
+        })
+        .await;
+
+    let mut results = Vec::new();
+    for segment_result in segment_results {
+        if results.is_empty() {
+            results = segment_result;
+        } else {
+            util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(a));
+        }
+    }
+    results
+}
+
+/// Compute the distance from a given path to some other path
+/// If there is no shared path, returns usize::MAX
+fn distance_between_paths(path: &Path, relative_to: &Path) -> usize {
+    let mut path_components = path.components();
+    let mut relative_components = relative_to.components();
+
+    while path_components
+        .next()
+        .zip(relative_components.next())
+        .map(|(path_component, relative_component)| path_component == relative_component)
+        .unwrap_or_default()
+    {}
+    path_components.count() + relative_components.count() + 1
+}
+
+#[cfg(test)]
+mod tests {
+    use std::path::Path;
+
+    use super::distance_between_paths;
+
+    #[test]
+    fn test_distance_between_paths_empty() {
+        distance_between_paths(Path::new(""), Path::new(""));
+    }
+}

crates/fuzzy2/src/strings.rs 🔗

@@ -0,0 +1,159 @@
+use crate::{
+    matcher::{Match, MatchCandidate, Matcher},
+    CharBag,
+};
+use gpui2::Executor;
+use std::{
+    borrow::Cow,
+    cmp::{self, Ordering},
+    sync::atomic::AtomicBool,
+};
+
+#[derive(Clone, Debug)]
+pub struct StringMatchCandidate {
+    pub id: usize,
+    pub string: String,
+    pub char_bag: CharBag,
+}
+
+impl Match for StringMatch {
+    fn score(&self) -> f64 {
+        self.score
+    }
+
+    fn set_positions(&mut self, positions: Vec<usize>) {
+        self.positions = positions;
+    }
+}
+
+impl StringMatchCandidate {
+    pub fn new(id: usize, string: String) -> Self {
+        Self {
+            id,
+            char_bag: CharBag::from(string.as_str()),
+            string,
+        }
+    }
+}
+
+impl<'a> MatchCandidate for &'a StringMatchCandidate {
+    fn has_chars(&self, bag: CharBag) -> bool {
+        self.char_bag.is_superset(bag)
+    }
+
+    fn to_string(&self) -> Cow<'a, str> {
+        self.string.as_str().into()
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct StringMatch {
+    pub candidate_id: usize,
+    pub score: f64,
+    pub positions: Vec<usize>,
+    pub string: String,
+}
+
+impl PartialEq for StringMatch {
+    fn eq(&self, other: &Self) -> bool {
+        self.cmp(other).is_eq()
+    }
+}
+
+impl Eq for StringMatch {}
+
+impl PartialOrd for StringMatch {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for StringMatch {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.score
+            .partial_cmp(&other.score)
+            .unwrap_or(Ordering::Equal)
+            .then_with(|| self.candidate_id.cmp(&other.candidate_id))
+    }
+}
+
+pub async fn match_strings(
+    candidates: &[StringMatchCandidate],
+    query: &str,
+    smart_case: bool,
+    max_results: usize,
+    cancel_flag: &AtomicBool,
+    executor: Executor,
+) -> Vec<StringMatch> {
+    if candidates.is_empty() || max_results == 0 {
+        return Default::default();
+    }
+
+    if query.is_empty() {
+        return candidates
+            .iter()
+            .map(|candidate| StringMatch {
+                candidate_id: candidate.id,
+                score: 0.,
+                positions: Default::default(),
+                string: candidate.string.clone(),
+            })
+            .collect();
+    }
+
+    let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
+    let query = query.chars().collect::<Vec<_>>();
+
+    let lowercase_query = &lowercase_query;
+    let query = &query;
+    let query_char_bag = CharBag::from(&lowercase_query[..]);
+
+    let num_cpus = executor.num_cpus().min(candidates.len());
+    let segment_size = (candidates.len() + num_cpus - 1) / num_cpus;
+    let mut segment_results = (0..num_cpus)
+        .map(|_| Vec::with_capacity(max_results.min(candidates.len())))
+        .collect::<Vec<_>>();
+
+    executor
+        .scoped(|scope| {
+            for (segment_idx, results) in segment_results.iter_mut().enumerate() {
+                let cancel_flag = &cancel_flag;
+                scope.spawn(async move {
+                    let segment_start = cmp::min(segment_idx * segment_size, candidates.len());
+                    let segment_end = cmp::min(segment_start + segment_size, candidates.len());
+                    let mut matcher = Matcher::new(
+                        query,
+                        lowercase_query,
+                        query_char_bag,
+                        smart_case,
+                        max_results,
+                    );
+
+                    matcher.match_candidates(
+                        &[],
+                        &[],
+                        candidates[segment_start..segment_end].iter(),
+                        results,
+                        cancel_flag,
+                        |candidate, score| StringMatch {
+                            candidate_id: candidate.id,
+                            score,
+                            positions: Vec::new(),
+                            string: candidate.string.to_string(),
+                        },
+                    );
+                });
+            }
+        })
+        .await;
+
+    let mut results = Vec::new();
+    for segment_result in segment_results {
+        if results.is_empty() {
+            results = segment_result;
+        } else {
+            util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(a));
+        }
+    }
+    results
+}

crates/gpui2/src/executor.rs 🔗

@@ -202,6 +202,20 @@ impl Executor {
         Task::Spawned(task)
     }
 
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn start_waiting(&self) {
+        todo!("start_waiting")
+    }
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub async fn simulate_random_delay(&self) {
+        todo!("simulate_random_delay")
+    }
+
+    pub fn num_cpus(&self) -> usize {
+        num_cpus::get()
+    }
+
     pub fn is_main_thread(&self) -> bool {
         self.dispatcher.is_main_thread()
     }

crates/language2/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = [
     "rand",
     "client2/test-support",
     "collections/test-support",
-    "lsp/test-support",
+    "lsp2/test-support",
     "text/test-support",
     "tree-sitter-rust",
     "tree-sitter-typescript",
@@ -24,16 +24,16 @@ test-support = [
 [dependencies]
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-fuzzy = { path = "../fuzzy" }
+fuzzy2 = { path = "../fuzzy2" }
 fs = { path = "../fs" }
 git = { path = "../git" }
 gpui2 = { path = "../gpui2" }
-lsp = { path = "../lsp" }
+lsp2 = { path = "../lsp2" }
 rpc = { path = "../rpc" }
 settings2 = { path = "../settings2" }
 sum_tree = { path = "../sum_tree" }
 text = { path = "../text" }
-# theme = { path = "../theme" }
+theme2 = { path = "../theme2" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -64,7 +64,7 @@ tree-sitter-typescript = { workspace = true, optional = true }
 client2 = { path = "../client2", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
 gpui2 = { path = "../gpui2", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
+lsp2 = { path = "../lsp2", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/language2/src/buffer.rs 🔗

@@ -17,7 +17,7 @@ use anyhow::{anyhow, Result};
 pub use clock::ReplicaId;
 use futures::FutureExt as _;
 use gpui2::{AppContext, EventEmitter, HighlightStyle, ModelContext, Task};
-use lsp::LanguageServerId;
+use lsp2::LanguageServerId;
 use parking_lot::Mutex;
 use similar::{ChangeTag, TextDiff};
 use smallvec::SmallVec;
@@ -40,6 +40,7 @@ use std::{
 use sum_tree::TreeMap;
 use text::operation_queue::OperationQueue;
 pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, *};
+use theme2::SyntaxTheme;
 #[cfg(any(test, feature = "test-support"))]
 use util::RandomCharIter;
 use util::{RangeExt, TryFutureExt as _};
@@ -47,7 +48,7 @@ use util::{RangeExt, TryFutureExt as _};
 #[cfg(any(test, feature = "test-support"))]
 pub use {tree_sitter_rust, tree_sitter_typescript};
 
-pub use lsp::DiagnosticSeverity;
+pub use lsp2::DiagnosticSeverity;
 
 pub struct Buffer {
     text: TextBuffer,
@@ -148,14 +149,14 @@ pub struct Completion {
     pub new_text: String,
     pub label: CodeLabel,
     pub server_id: LanguageServerId,
-    pub lsp_completion: lsp::CompletionItem,
+    pub lsp_completion: lsp2::CompletionItem,
 }
 
 #[derive(Clone, Debug)]
 pub struct CodeAction {
     pub server_id: LanguageServerId,
     pub range: Range<Anchor>,
-    pub lsp_action: lsp::CodeAction,
+    pub lsp_action: lsp2::CodeAction,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -3000,14 +3001,14 @@ impl IndentSize {
 impl Completion {
     pub fn sort_key(&self) -> (usize, &str) {
         let kind_key = match self.lsp_completion.kind {
-            Some(lsp::CompletionItemKind::VARIABLE) => 0,
+            Some(lsp2::CompletionItemKind::VARIABLE) => 0,
             _ => 1,
         };
         (kind_key, &self.label.text[self.label.filter_range.clone()])
     }
 
     pub fn is_snippet(&self) -> bool {
-        self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
+        self.lsp_completion.insert_text_format == Some(lsp2::InsertTextFormat::SNIPPET)
     }
 }
 

crates/language2/src/buffer_tests.rs 🔗

@@ -1,2442 +1,2442 @@
-use crate::language_settings::{
-    AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
-};
-
-use super::*;
-use clock::ReplicaId;
-use collections::BTreeMap;
-use gpui::{AppContext, ModelHandle};
-use indoc::indoc;
-use proto::deserialize_operation;
-use rand::prelude::*;
-use regex::RegexBuilder;
-use settings::SettingsStore;
-use std::{
-    cell::RefCell,
-    env,
-    ops::Range,
-    rc::Rc,
-    time::{Duration, Instant},
-};
-use text::network::Network;
-use text::LineEnding;
-use unindent::Unindent as _;
-use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
-
-lazy_static! {
-    static ref TRAILING_WHITESPACE_REGEX: Regex = RegexBuilder::new("[ \t]+$")
-        .multi_line(true)
-        .build()
-        .unwrap();
-}
-
-#[cfg(test)]
-#[ctor::ctor]
-fn init_logger() {
-    if std::env::var("RUST_LOG").is_ok() {
-        env_logger::init();
-    }
-}
-
-#[gpui::test]
-fn test_line_endings(cx: &mut gpui::AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let mut buffer = Buffer::new(0, cx.model_id() as u64, "one\r\ntwo\rthree")
-            .with_language(Arc::new(rust_lang()), cx);
-        assert_eq!(buffer.text(), "one\ntwo\nthree");
-        assert_eq!(buffer.line_ending(), LineEnding::Windows);
-
-        buffer.check_invariants();
-        buffer.edit(
-            [(buffer.len()..buffer.len(), "\r\nfour")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        buffer.edit([(0..0, "zero\r\n")], None, cx);
-        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
-        assert_eq!(buffer.line_ending(), LineEnding::Windows);
-        buffer.check_invariants();
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_select_language() {
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(Arc::new(Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    )));
-    registry.add(Arc::new(Language::new(
-        LanguageConfig {
-            name: "Make".into(),
-            path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    )));
-
-    // matching file extension
-    assert_eq!(
-        registry
-            .language_for_file("zed/lib.rs", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        Some("Rust".into())
-    );
-    assert_eq!(
-        registry
-            .language_for_file("zed/lib.mk", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        Some("Make".into())
-    );
-
-    // matching filename
-    assert_eq!(
-        registry
-            .language_for_file("zed/Makefile", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        Some("Make".into())
-    );
-
-    // matching suffix that is not the full file extension or filename
-    assert_eq!(
-        registry
-            .language_for_file("zed/cars", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        None
-    );
-    assert_eq!(
-        registry
-            .language_for_file("zed/a.cars", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        None
-    );
-    assert_eq!(
-        registry
-            .language_for_file("zed/sumk", None)
-            .now_or_never()
-            .and_then(|l| Some(l.ok()?.name())),
-        None
-    );
-}
-
-#[gpui::test]
-fn test_edit_events(cx: &mut gpui::AppContext) {
-    let mut now = Instant::now();
-    let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
-    let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
-
-    let buffer1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abcdef"));
-    let buffer2 = cx.add_model(|cx| Buffer::new(1, cx.model_id() as u64, "abcdef"));
-    let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
-    buffer1.update(cx, {
-        let buffer1_ops = buffer1_ops.clone();
-        |buffer, cx| {
-            let buffer_1_events = buffer_1_events.clone();
-            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
-                Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
-                event => buffer_1_events.borrow_mut().push(event),
-            })
-            .detach();
-            let buffer_2_events = buffer_2_events.clone();
-            cx.subscribe(&buffer2, move |_, _, event, _| {
-                buffer_2_events.borrow_mut().push(event.clone())
-            })
-            .detach();
-
-            // An edit emits an edited event, followed by a dirty changed event,
-            // since the buffer was previously in a clean state.
-            buffer.edit([(2..4, "XYZ")], None, cx);
-
-            // An empty transaction does not emit any events.
-            buffer.start_transaction();
-            buffer.end_transaction(cx);
-
-            // A transaction containing two edits emits one edited event.
-            now += Duration::from_secs(1);
-            buffer.start_transaction_at(now);
-            buffer.edit([(5..5, "u")], None, cx);
-            buffer.edit([(6..6, "w")], None, cx);
-            buffer.end_transaction_at(now, cx);
-
-            // Undoing a transaction emits one edited event.
-            buffer.undo(cx);
-        }
-    });
-
-    // Incorporating a set of remote ops emits a single edited event,
-    // followed by a dirty changed event.
-    buffer2.update(cx, |buffer, cx| {
-        buffer
-            .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
-            .unwrap();
-    });
-    assert_eq!(
-        mem::take(&mut *buffer_1_events.borrow_mut()),
-        vec![
-            Event::Edited,
-            Event::DirtyChanged,
-            Event::Edited,
-            Event::Edited,
-        ]
-    );
-    assert_eq!(
-        mem::take(&mut *buffer_2_events.borrow_mut()),
-        vec![Event::Edited, Event::DirtyChanged]
-    );
-
-    buffer1.update(cx, |buffer, cx| {
-        // Undoing the first transaction emits edited event, followed by a
-        // dirty changed event, since the buffer is again in a clean state.
-        buffer.undo(cx);
-    });
-    // Incorporating the remote ops again emits a single edited event,
-    // followed by a dirty changed event.
-    buffer2.update(cx, |buffer, cx| {
-        buffer
-            .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
-            .unwrap();
-    });
-    assert_eq!(
-        mem::take(&mut *buffer_1_events.borrow_mut()),
-        vec![Event::Edited, Event::DirtyChanged,]
-    );
-    assert_eq!(
-        mem::take(&mut *buffer_2_events.borrow_mut()),
-        vec![Event::Edited, Event::DirtyChanged]
-    );
-}
-
-#[gpui::test]
-async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
-    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
-    let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
-
-    let text = "a\nccc\ndddd\nffffff\n";
-    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
-    buffer.update(cx, |buffer, cx| {
-        buffer.apply_diff(diff, cx).unwrap();
-        assert_eq!(buffer.text(), text);
-        assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
-    });
-
-    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
-    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
-    buffer.update(cx, |buffer, cx| {
-        buffer.apply_diff(diff, cx).unwrap();
-        assert_eq!(buffer.text(), text);
-        assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
-    });
-}
-
-#[gpui::test(iterations = 10)]
-async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
-    let text = [
-        "zero",     //
-        "one  ",    // 2 trailing spaces
-        "two",      //
-        "three   ", // 3 trailing spaces
-        "four",     //
-        "five    ", // 4 trailing spaces
-    ]
-    .join("\n");
-
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
-
-    // Spawn a task to format the buffer's whitespace.
-    // Pause so that the foratting task starts running.
-    let format = buffer.read_with(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
-    smol::future::yield_now().await;
-
-    // Edit the buffer while the normalization task is running.
-    let version_before_edit = buffer.read_with(cx, |buffer, _| buffer.version());
-    buffer.update(cx, |buffer, cx| {
-        buffer.edit(
-            [
-                (Point::new(0, 1)..Point::new(0, 1), "EE"),
-                (Point::new(3, 5)..Point::new(3, 5), "EEE"),
-            ],
-            None,
-            cx,
-        );
-    });
-
-    let format_diff = format.await;
-    buffer.update(cx, |buffer, cx| {
-        let version_before_format = format_diff.base_version.clone();
-        buffer.apply_diff(format_diff, cx);
-
-        // The outcome depends on the order of concurrent taks.
-        //
-        // If the edit occurred while searching for trailing whitespace ranges,
-        // then the trailing whitespace region touched by the edit is left intact.
-        if version_before_format == version_before_edit {
-            assert_eq!(
-                buffer.text(),
-                [
-                    "zEEero",      //
-                    "one",         //
-                    "two",         //
-                    "threeEEE   ", //
-                    "four",        //
-                    "five",        //
-                ]
-                .join("\n")
-            );
-        }
-        // Otherwise, all trailing whitespace is removed.
-        else {
-            assert_eq!(
-                buffer.text(),
-                [
-                    "zEEero",   //
-                    "one",      //
-                    "two",      //
-                    "threeEEE", //
-                    "four",     //
-                    "five",     //
-                ]
-                .join("\n")
-            );
-        }
-    });
-}
-
-#[gpui::test]
-async fn test_reparse(cx: &mut gpui::TestAppContext) {
-    let text = "fn a() {}";
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
-    });
-
-    // Wait for the initial text to parse
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        concat!(
-            "(source_file (function_item name: (identifier) ",
-            "parameters: (parameters) ",
-            "body: (block)))"
-        )
-    );
-
-    buffer.update(cx, |buffer, _| {
-        buffer.set_sync_parse_timeout(Duration::ZERO)
-    });
-
-    // Perform some edits (add parameter and variable reference)
-    // Parsing doesn't begin until the transaction is complete
-    buffer.update(cx, |buf, cx| {
-        buf.start_transaction();
-
-        let offset = buf.text().find(')').unwrap();
-        buf.edit([(offset..offset, "b: C")], None, cx);
-        assert!(!buf.is_parsing());
-
-        let offset = buf.text().find('}').unwrap();
-        buf.edit([(offset..offset, " d; ")], None, cx);
-        assert!(!buf.is_parsing());
-
-        buf.end_transaction(cx);
-        assert_eq!(buf.text(), "fn a(b: C) { d; }");
-        assert!(buf.is_parsing());
-    });
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        concat!(
-            "(source_file (function_item name: (identifier) ",
-            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
-            "body: (block (expression_statement (identifier)))))"
-        )
-    );
-
-    // Perform a series of edits without waiting for the current parse to complete:
-    // * turn identifier into a field expression
-    // * turn field expression into a method call
-    // * add a turbofish to the method call
-    buffer.update(cx, |buf, cx| {
-        let offset = buf.text().find(';').unwrap();
-        buf.edit([(offset..offset, ".e")], None, cx);
-        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
-        assert!(buf.is_parsing());
-    });
-    buffer.update(cx, |buf, cx| {
-        let offset = buf.text().find(';').unwrap();
-        buf.edit([(offset..offset, "(f)")], None, cx);
-        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
-        assert!(buf.is_parsing());
-    });
-    buffer.update(cx, |buf, cx| {
-        let offset = buf.text().find("(f)").unwrap();
-        buf.edit([(offset..offset, "::<G>")], None, cx);
-        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
-        assert!(buf.is_parsing());
-    });
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        concat!(
-            "(source_file (function_item name: (identifier) ",
-            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
-            "body: (block (expression_statement (call_expression ",
-            "function: (generic_function ",
-            "function: (field_expression value: (identifier) field: (field_identifier)) ",
-            "type_arguments: (type_arguments (type_identifier))) ",
-            "arguments: (arguments (identifier)))))))",
-        )
-    );
-
-    buffer.update(cx, |buf, cx| {
-        buf.undo(cx);
-        buf.undo(cx);
-        buf.undo(cx);
-        buf.undo(cx);
-        assert_eq!(buf.text(), "fn a() {}");
-        assert!(buf.is_parsing());
-    });
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        concat!(
-            "(source_file (function_item name: (identifier) ",
-            "parameters: (parameters) ",
-            "body: (block)))"
-        )
-    );
-
-    buffer.update(cx, |buf, cx| {
-        buf.redo(cx);
-        buf.redo(cx);
-        buf.redo(cx);
-        buf.redo(cx);
-        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
-        assert!(buf.is_parsing());
-    });
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        concat!(
-            "(source_file (function_item name: (identifier) ",
-            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
-            "body: (block (expression_statement (call_expression ",
-            "function: (generic_function ",
-            "function: (field_expression value: (identifier) field: (field_identifier)) ",
-            "type_arguments: (type_arguments (type_identifier))) ",
-            "arguments: (arguments (identifier)))))))",
-        )
-    );
-}
-
-#[gpui::test]
-async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
-    let buffer = cx.add_model(|cx| {
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, "{}").with_language(Arc::new(rust_lang()), cx);
-        buffer.set_sync_parse_timeout(Duration::ZERO);
-        buffer
-    });
-
-    // Wait for the initial text to parse
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(
-        get_tree_sexp(&buffer, cx),
-        "(source_file (expression_statement (block)))"
-    );
-
-    buffer.update(cx, |buffer, cx| {
-        buffer.set_language(Some(Arc::new(json_lang())), cx)
-    });
-    buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
-    assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
-}
-
-#[gpui::test]
-async fn test_outline(cx: &mut gpui::TestAppContext) {
-    let text = r#"
-        struct Person {
-            name: String,
-            age: usize,
-        }
-
-        mod module {
-            enum LoginState {
-                LoggedOut,
-                LoggingOn,
-                LoggedIn {
-                    person: Person,
-                    time: Instant,
-                }
-            }
-        }
-
-        impl Eq for Person {}
-
-        impl Drop for Person {
-            fn drop(&mut self) {
-                println!("bye");
-            }
-        }
-    "#
-    .unindent();
-
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
-    });
-    let outline = buffer
-        .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
-        .unwrap();
-
-    assert_eq!(
-        outline
-            .items
-            .iter()
-            .map(|item| (item.text.as_str(), item.depth))
-            .collect::<Vec<_>>(),
-        &[
-            ("struct Person", 0),
-            ("name", 1),
-            ("age", 1),
-            ("mod module", 0),
-            ("enum LoginState", 1),
-            ("LoggedOut", 2),
-            ("LoggingOn", 2),
-            ("LoggedIn", 2),
-            ("person", 3),
-            ("time", 3),
-            ("impl Eq for Person", 0),
-            ("impl Drop for Person", 0),
-            ("fn drop", 1),
-        ]
-    );
-
-    // Without space, we only match on names
-    assert_eq!(
-        search(&outline, "oon", cx).await,
-        &[
-            ("mod module", vec![]),                    // included as the parent of a match
-            ("enum LoginState", vec![]),               // included as the parent of a match
-            ("LoggingOn", vec![1, 7, 8]),              // matches
-            ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
-        ]
-    );
-
-    assert_eq!(
-        search(&outline, "dp p", cx).await,
-        &[
-            ("impl Drop for Person", vec![5, 8, 9, 14]),
-            ("fn drop", vec![]),
-        ]
-    );
-    assert_eq!(
-        search(&outline, "dpn", cx).await,
-        &[("impl Drop for Person", vec![5, 14, 19])]
-    );
-    assert_eq!(
-        search(&outline, "impl ", cx).await,
-        &[
-            ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
-            ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
-            ("fn drop", vec![]),
-        ]
-    );
-
-    async fn search<'a>(
-        outline: &'a Outline<Anchor>,
-        query: &'a str,
-        cx: &'a gpui::TestAppContext,
-    ) -> Vec<(&'a str, Vec<usize>)> {
-        let matches = cx
-            .read(|cx| outline.search(query, cx.background().clone()))
-            .await;
-        matches
-            .into_iter()
-            .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
-            .collect::<Vec<_>>()
-    }
-}
-
-#[gpui::test]
-async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
-    let text = r#"
-        impl A for B<
-            C
-        > {
-        };
-    "#
-    .unindent();
-
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
-    });
-    let outline = buffer
-        .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
-        .unwrap();
-
-    assert_eq!(
-        outline
-            .items
-            .iter()
-            .map(|item| (item.text.as_str(), item.depth))
-            .collect::<Vec<_>>(),
-        &[("impl A for B<", 0)]
-    );
-}
-
-#[gpui::test]
-async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
-    let language = javascript_lang()
-        .with_outline_query(
-            r#"
-            (function_declaration
-                "function" @context
-                name: (_) @name
-                parameters: (formal_parameters
-                    "(" @context.extra
-                    ")" @context.extra)) @item
-            "#,
-        )
-        .unwrap();
-
-    let text = r#"
-        function a() {}
-        function b(c) {}
-    "#
-    .unindent();
-
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(language), cx)
-    });
-    let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
-
-    // extra context nodes are included in the outline.
-    let outline = snapshot.outline(None).unwrap();
-    assert_eq!(
-        outline
-            .items
-            .iter()
-            .map(|item| (item.text.as_str(), item.depth))
-            .collect::<Vec<_>>(),
-        &[("function a()", 0), ("function b( )", 0),]
-    );
-
-    // extra context nodes do not appear in breadcrumbs.
-    let symbols = snapshot.symbols_containing(3, None).unwrap();
-    assert_eq!(
-        symbols
-            .iter()
-            .map(|item| (item.text.as_str(), item.depth))
-            .collect::<Vec<_>>(),
-        &[("function a", 0)]
-    );
-}
-
-#[gpui::test]
-async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
-    let text = r#"
-        impl Person {
-            fn one() {
-                1
-            }
-
-            fn two() {
-                2
-            }fn three() {
-                3
-            }
-        }
-    "#
-    .unindent();
-
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
-    });
-    let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
-
-    // point is at the start of an item
-    assert_eq!(
-        symbols_containing(Point::new(1, 4), &snapshot),
-        vec![
-            (
-                "impl Person".to_string(),
-                Point::new(0, 0)..Point::new(10, 1)
-            ),
-            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
-        ]
-    );
-
-    // point is in the middle of an item
-    assert_eq!(
-        symbols_containing(Point::new(2, 8), &snapshot),
-        vec![
-            (
-                "impl Person".to_string(),
-                Point::new(0, 0)..Point::new(10, 1)
-            ),
-            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
-        ]
-    );
-
-    // point is at the end of an item
-    assert_eq!(
-        symbols_containing(Point::new(3, 5), &snapshot),
-        vec![
-            (
-                "impl Person".to_string(),
-                Point::new(0, 0)..Point::new(10, 1)
-            ),
-            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
-        ]
-    );
-
-    // point is in between two adjacent items
-    assert_eq!(
-        symbols_containing(Point::new(7, 5), &snapshot),
-        vec![
-            (
-                "impl Person".to_string(),
-                Point::new(0, 0)..Point::new(10, 1)
-            ),
-            ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
-        ]
-    );
-
-    fn symbols_containing(
-        position: Point,
-        snapshot: &BufferSnapshot,
-    ) -> Vec<(String, Range<Point>)> {
-        snapshot
-            .symbols_containing(position, None)
-            .unwrap()
-            .into_iter()
-            .map(|item| {
-                (
-                    item.text,
-                    item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
-                )
-            })
-            .collect()
-    }
-}
-
-#[gpui::test]
-fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
-    let mut assert = |selection_text, range_markers| {
-        assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
-    };
-
-    assert(
-        indoc! {"
-            mod x {
-                moˇd y {
-
-                }
-            }
-            let foo = 1;"},
-        vec![indoc! {"
-            mod x «{»
-                mod y {
-
-                }
-            «}»
-            let foo = 1;"}],
-    );
-
-    assert(
-        indoc! {"
-            mod x {
-                mod y ˇ{
-
-                }
-            }
-            let foo = 1;"},
-        vec![
-            indoc! {"
-                mod x «{»
-                    mod y {
-
-                    }
-                «}»
-                let foo = 1;"},
-            indoc! {"
-                mod x {
-                    mod y «{»
-
-                    «}»
-                }
-                let foo = 1;"},
-        ],
-    );
-
-    assert(
-        indoc! {"
-            mod x {
-                mod y {
-
-                }ˇ
-            }
-            let foo = 1;"},
-        vec![
-            indoc! {"
-                mod x «{»
-                    mod y {
-
-                    }
-                «}»
-                let foo = 1;"},
-            indoc! {"
-                mod x {
-                    mod y «{»
-
-                    «}»
-                }
-                let foo = 1;"},
-        ],
-    );
-
-    assert(
-        indoc! {"
-            mod x {
-                mod y {
-
-                }
-            ˇ}
-            let foo = 1;"},
-        vec![indoc! {"
-            mod x «{»
-                mod y {
-
-                }
-            «}»
-            let foo = 1;"}],
-    );
-
-    assert(
-        indoc! {"
-            mod x {
-                mod y {
-
-                }
-            }
-            let fˇoo = 1;"},
-        vec![],
-    );
-
-    // Regression test: avoid crash when querying at the end of the buffer.
-    assert(
-        indoc! {"
-            mod x {
-                mod y {
-
-                }
-            }
-            let foo = 1;ˇ"},
-        vec![],
-    );
-}
-
-#[gpui::test]
-fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
-    let mut assert = |selection_text, bracket_pair_texts| {
-        assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
-    };
-
-    assert(
-        indoc! {"
-        for (const a in b)ˇ {
-            // a comment that's longer than the for-loop header
-        }"},
-        vec![indoc! {"
-        for «(»const a in b«)» {
-            // a comment that's longer than the for-loop header
-        }"}],
-    );
-
-    // Regression test: even though the parent node of the parentheses (the for loop) does
-    // intersect the given range, the parentheses themselves do not contain the range, so
-    // they should not be returned. Only the curly braces contain the range.
-    assert(
-        indoc! {"
-        for (const a in b) {ˇ
-            // a comment that's longer than the for-loop header
-        }"},
-        vec![indoc! {"
-        for (const a in b) «{»
-            // a comment that's longer than the for-loop header
-        «}»"}],
-    );
-}
-
-#[gpui::test]
-fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
-    cx.add_model(|cx| {
-        let text = "fn a() { b(|c| {}) }";
-        let buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-        let snapshot = buffer.snapshot();
-
-        assert_eq!(
-            snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
-            Some(range_of(text, "|"))
-        );
-        assert_eq!(
-            snapshot.range_for_syntax_ancestor(range_of(text, "|")),
-            Some(range_of(text, "|c|"))
-        );
-        assert_eq!(
-            snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
-            Some(range_of(text, "|c| {}"))
-        );
-        assert_eq!(
-            snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
-            Some(range_of(text, "(|c| {})"))
-        );
-
-        buffer
-    });
-
-    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
-        let start = text.find(part).unwrap();
-        start..start
-    }
-
-    fn range_of(text: &str, part: &str) -> Range<usize> {
-        let start = text.find(part).unwrap();
-        start..start + part.len()
-    }
-}
-
-#[gpui::test]
-fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = "fn a() {}";
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-
-        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
-        assert_eq!(buffer.text(), "fn a() {\n    \n}");
-
-        buffer.edit(
-            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
-
-        // Create a field expression on a new line, causing that line
-        // to be indented.
-        buffer.edit(
-            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
-
-        // Remove the dot so that the line is no longer a field expression,
-        // causing the line to be outdented.
-        buffer.edit(
-            [(Point::new(2, 8)..Point::new(2, 9), "")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
-    init_settings(cx, |settings| {
-        settings.defaults.hard_tabs = Some(true);
-    });
-
-    cx.add_model(|cx| {
-        let text = "fn a() {}";
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-
-        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
-        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
-
-        buffer.edit(
-            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
-
-        // Create a field expression on a new line, causing that line
-        // to be indented.
-        buffer.edit(
-            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
-
-        // Remove the dot so that the line is no longer a field expression,
-        // causing the line to be outdented.
-        buffer.edit(
-            [(Point::new(2, 2)..Point::new(2, 3), "")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let mut buffer = Buffer::new(
-            0,
-            cx.model_id() as u64,
-            "
-            fn a() {
-            c;
-            d;
-            }
-            "
-            .unindent(),
-        )
-        .with_language(Arc::new(rust_lang()), cx);
-
-        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
-        // their indentation is not adjusted.
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-            c«()»;
-            d«()»;
-            }
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-            c();
-            d();
-            }
-            "
-            .unindent()
-        );
-
-        // When appending new content after these lines, the indentation is based on the
-        // preceding lines' actual indentation.
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-            c«
-            .f
-            .g()»;
-            d«
-            .f
-            .g()»;
-            }
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-            c
-                .f
-                .g();
-            d
-                .f
-                .g();
-            }
-            "
-            .unindent()
-        );
-        buffer
-    });
-
-    cx.add_model(|cx| {
-        let mut buffer = Buffer::new(
-            0,
-            cx.model_id() as u64,
-            "
-            fn a() {
-                b();
-                |
-            "
-            .replace("|", "") // marker to preserve trailing whitespace
-            .unindent(),
-        )
-        .with_language(Arc::new(rust_lang()), cx);
-
-        // Insert a closing brace. It is outdented.
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-                b();
-                «}»
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-                b();
-            }
-            "
-            .unindent()
-        );
-
-        // Manually edit the leading whitespace. The edit is preserved.
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-                b();
-            «    »}
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-                b();
-                }
-            "
-            .unindent()
-        );
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let mut buffer = Buffer::new(
-            0,
-            cx.model_id() as u64,
-            "
-            fn a() {
-                i
-            }
-            "
-            .unindent(),
-        )
-        .with_language(Arc::new(rust_lang()), cx);
-
-        // Regression test: line does not get outdented due to syntax error
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-                i«f let Some(x) = y»
-            }
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-                if let Some(x) = y
-            }
-            "
-            .unindent()
-        );
-
-        buffer.edit_via_marked_text(
-            &"
-            fn a() {
-                if let Some(x) = y« {»
-            }
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a() {
-                if let Some(x) = y {
-            }
-            "
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let mut buffer = Buffer::new(
-            0,
-            cx.model_id() as u64,
-            "
-            fn a() {}
-            "
-            .unindent(),
-        )
-        .with_language(Arc::new(rust_lang()), cx);
-
-        buffer.edit_via_marked_text(
-            &"
-            fn a(«
-            b») {}
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            fn a(
-                b) {}
-            "
-            .unindent()
-        );
-
-        // The indentation suggestion changed because `@end` node (a close paren)
-        // is now at the beginning of the line.
-        buffer.edit_via_marked_text(
-            &"
-            fn a(
-                ˇ) {}
-            "
-            .unindent(),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-                fn a(
-                ) {}
-            "
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = "a\nb";
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-        buffer.edit(
-            [(0..1, "\n"), (2..3, "\n")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(buffer.text(), "\n\n\n");
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = "
-            const a: usize = 1;
-            fn b() {
-                if c {
-                    let d = 2;
-                }
-            }
-        "
-        .unindent();
-
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-        buffer.edit(
-            [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-                const a: usize = 1;
-                fn b() {
-                    if c {
-                        e(
-                            f()
-                        );
-                        let d = 2;
-                    }
-                }
-            "
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_block_mode(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = r#"
-            fn a() {
-                b();
-            }
-        "#
-        .unindent();
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-
-        // When this text was copied, both of the quotation marks were at the same
-        // indent level, but the indentation of the first line was not included in
-        // the copied text. This information is retained in the
-        // 'original_indent_columns' vector.
-        let original_indent_columns = vec![4];
-        let inserted_text = r#"
-            "
-                  c
-                    d
-                      e
-                "
-        "#
-        .unindent();
-
-        // Insert the block at column zero. The entire block is indented
-        // so that the first line matches the previous line's indentation.
-        buffer.edit(
-            [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
-            Some(AutoindentMode::Block {
-                original_indent_columns: original_indent_columns.clone(),
-            }),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            r#"
-            fn a() {
-                b();
-                "
-                  c
-                    d
-                      e
-                "
-            }
-            "#
-            .unindent()
-        );
-
-        // Grouping is disabled in tests, so we need 2 undos
-        buffer.undo(cx); // Undo the auto-indent
-        buffer.undo(cx); // Undo the original edit
-
-        // Insert the block at a deeper indent level. The entire block is outdented.
-        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
-        buffer.edit(
-            [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
-            Some(AutoindentMode::Block {
-                original_indent_columns: original_indent_columns.clone(),
-            }),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            r#"
-            fn a() {
-                b();
-                "
-                  c
-                    d
-                      e
-                "
-            }
-            "#
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = r#"
-            fn a() {
-                if b() {
-
-                }
-            }
-        "#
-        .unindent();
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
-
-        // The original indent columns are not known, so this text is
-        // auto-indented in a block as if the first line was copied in
-        // its entirety.
-        let original_indent_columns = Vec::new();
-        let inserted_text = "    c\n        .d()\n        .e();";
-
-        // Insert the block at column zero. The entire block is indented
-        // so that the first line matches the previous line's indentation.
-        buffer.edit(
-            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
-            Some(AutoindentMode::Block {
-                original_indent_columns: original_indent_columns.clone(),
-            }),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            r#"
-            fn a() {
-                if b() {
-                    c
-                        .d()
-                        .e();
-                }
-            }
-            "#
-            .unindent()
-        );
-
-        // Grouping is disabled in tests, so we need 2 undos
-        buffer.undo(cx); // Undo the auto-indent
-        buffer.undo(cx); // Undo the original edit
-
-        // Insert the block at a deeper indent level. The entire block is outdented.
-        buffer.edit(
-            [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
-            None,
-            cx,
-        );
-        buffer.edit(
-            [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
-            Some(AutoindentMode::Block {
-                original_indent_columns: Vec::new(),
-            }),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            r#"
-            fn a() {
-                if b() {
-                    c
-                        .d()
-                        .e();
-                }
-            }
-            "#
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = "
-            * one
-                - a
-                - b
-            * two
-        "
-        .unindent();
-
-        let mut buffer = Buffer::new(0, cx.model_id() as u64, text).with_language(
-            Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Markdown".into(),
-                    auto_indent_using_last_non_empty_line: false,
-                    ..Default::default()
-                },
-                Some(tree_sitter_json::language()),
-            )),
-            cx,
-        );
-        buffer.edit(
-            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-            * one
-                - a
-                - b
-
-            * two
-            "
-            .unindent()
-        );
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
-    init_settings(cx, |settings| {
-        settings.languages.extend([
-            (
-                "HTML".into(),
-                LanguageSettingsContent {
-                    tab_size: Some(2.try_into().unwrap()),
-                    ..Default::default()
-                },
-            ),
-            (
-                "JavaScript".into(),
-                LanguageSettingsContent {
-                    tab_size: Some(8.try_into().unwrap()),
-                    ..Default::default()
-                },
-            ),
-        ])
-    });
-
-    let html_language = Arc::new(html_lang());
-
-    let javascript_language = Arc::new(javascript_lang());
-
-    let language_registry = Arc::new(LanguageRegistry::test());
-    language_registry.add(html_language.clone());
-    language_registry.add(javascript_language.clone());
-
-    cx.add_model(|cx| {
-        let (text, ranges) = marked_text_ranges(
-            &"
-                <div>ˇ
-                </div>
-                <script>
-                    init({ˇ
-                    })
-                </script>
-                <span>ˇ
-                </span>
-            "
-            .unindent(),
-            false,
-        );
-
-        let mut buffer = Buffer::new(0, cx.model_id() as u64, text);
-        buffer.set_language_registry(language_registry);
-        buffer.set_language(Some(html_language), cx);
-        buffer.edit(
-            ranges.into_iter().map(|range| (range, "\na")),
-            Some(AutoindentMode::EachLine),
-            cx,
-        );
-        assert_eq!(
-            buffer.text(),
-            "
-                <div>
-                  a
-                </div>
-                <script>
-                    init({
-                            a
-                    })
-                </script>
-                <span>
-                  a
-                </span>
-            "
-            .unindent()
-        );
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
-    init_settings(cx, |settings| {
-        settings.defaults.tab_size = Some(2.try_into().unwrap());
-    });
-
-    cx.add_model(|cx| {
-        let mut buffer =
-            Buffer::new(0, cx.model_id() as u64, "").with_language(Arc::new(ruby_lang()), cx);
-
-        let text = r#"
-            class C
-            def a(b, c)
-            puts b
-            puts c
-            rescue
-            puts "errored"
-            exit 1
-            end
-            end
-        "#
-        .unindent();
-
-        buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
-
-        assert_eq!(
-            buffer.text(),
-            r#"
-                class C
-                  def a(b, c)
-                    puts b
-                    puts c
-                  rescue
-                    puts "errored"
-                    exit 1
-                  end
-                end
-            "#
-            .unindent()
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let language = Language::new(
-            LanguageConfig {
-                name: "JavaScript".into(),
-                line_comment: Some("// ".into()),
-                brackets: BracketPairConfig {
-                    pairs: vec![
-                        BracketPair {
-                            start: "{".into(),
-                            end: "}".into(),
-                            close: true,
-                            newline: false,
-                        },
-                        BracketPair {
-                            start: "'".into(),
-                            end: "'".into(),
-                            close: true,
-                            newline: false,
-                        },
-                    ],
-                    disabled_scopes_by_bracket_ix: vec![
-                        Vec::new(), //
-                        vec!["string".into()],
-                    ],
-                },
-                overrides: [(
-                    "element".into(),
-                    LanguageConfigOverride {
-                        line_comment: Override::Remove { remove: true },
-                        block_comment: Override::Set(("{/*".into(), "*/}".into())),
-                        ..Default::default()
-                    },
-                )]
-                .into_iter()
-                .collect(),
-                ..Default::default()
-            },
-            Some(tree_sitter_typescript::language_tsx()),
-        )
-        .with_override_query(
-            r#"
-                (jsx_element) @element
-                (string) @string
-            "#,
-        )
-        .unwrap();
-
-        let text = r#"a["b"] = <C d="e"></C>;"#;
-
-        let buffer =
-            Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(language), cx);
-        let snapshot = buffer.snapshot();
-
-        let config = snapshot.language_scope_at(0).unwrap();
-        assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
-        // Both bracket pairs are enabled
-        assert_eq!(
-            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
-            &[true, true]
-        );
-
-        let string_config = snapshot.language_scope_at(3).unwrap();
-        assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
-        // Second bracket pair is disabled
-        assert_eq!(
-            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
-            &[true, false]
-        );
-
-        let element_config = snapshot.language_scope_at(10).unwrap();
-        assert_eq!(element_config.line_comment_prefix(), None);
-        assert_eq!(
-            element_config.block_comment_delimiters(),
-            Some((&"{/*".into(), &"*/}".into()))
-        );
-        // Both bracket pairs are enabled
-        assert_eq!(
-            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
-            &[true, true]
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_language_scope_at_with_rust(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                brackets: BracketPairConfig {
-                    pairs: vec![
-                        BracketPair {
-                            start: "{".into(),
-                            end: "}".into(),
-                            close: true,
-                            newline: false,
-                        },
-                        BracketPair {
-                            start: "'".into(),
-                            end: "'".into(),
-                            close: true,
-                            newline: false,
-                        },
-                    ],
-                    disabled_scopes_by_bracket_ix: vec![
-                        Vec::new(), //
-                        vec!["string".into()],
-                    ],
-                },
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )
-        .with_override_query(
-            r#"
-                (string_literal) @string
-            "#,
-        )
-        .unwrap();
-
-        let text = r#"
-            const S: &'static str = "hello";
-        "#
-        .unindent();
-
-        let buffer = Buffer::new(0, cx.model_id() as u64, text.clone())
-            .with_language(Arc::new(language), cx);
-        let snapshot = buffer.snapshot();
-
-        // By default, all brackets are enabled
-        let config = snapshot.language_scope_at(0).unwrap();
-        assert_eq!(
-            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
-            &[true, true]
-        );
-
-        // Within a string, the quotation brackets are disabled.
-        let string_config = snapshot
-            .language_scope_at(text.find("ello").unwrap())
-            .unwrap();
-        assert_eq!(
-            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
-            &[true, false]
-        );
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
-    init_settings(cx, |_| {});
-
-    cx.add_model(|cx| {
-        let text = r#"
-            <ol>
-            <% people.each do |person| %>
-                <li>
-                    <%= person.name %>
-                </li>
-            <% end %>
-            </ol>
-        "#
-        .unindent();
-
-        let language_registry = Arc::new(LanguageRegistry::test());
-        language_registry.add(Arc::new(ruby_lang()));
-        language_registry.add(Arc::new(html_lang()));
-        language_registry.add(Arc::new(erb_lang()));
-
-        let mut buffer = Buffer::new(0, cx.model_id() as u64, text);
-        buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(
-            language_registry
-                .language_for_name("ERB")
-                .now_or_never()
-                .unwrap()
-                .ok(),
-            cx,
-        );
-
-        let snapshot = buffer.snapshot();
-        let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
-        assert_eq!(html_config.line_comment_prefix(), None);
-        assert_eq!(
-            html_config.block_comment_delimiters(),
-            Some((&"<!--".into(), &"-->".into()))
-        );
-
-        let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
-        assert_eq!(ruby_config.line_comment_prefix().unwrap().as_ref(), "# ");
-        assert_eq!(ruby_config.block_comment_delimiters(), None);
-
-        buffer
-    });
-}
-
-#[gpui::test]
-fn test_serialization(cx: &mut gpui::AppContext) {
-    let mut now = Instant::now();
-
-    let buffer1 = cx.add_model(|cx| {
-        let mut buffer = Buffer::new(0, cx.model_id() as u64, "abc");
-        buffer.edit([(3..3, "D")], None, cx);
-
-        now += Duration::from_secs(1);
-        buffer.start_transaction_at(now);
-        buffer.edit([(4..4, "E")], None, cx);
-        buffer.end_transaction_at(now, cx);
-        assert_eq!(buffer.text(), "abcDE");
-
-        buffer.undo(cx);
-        assert_eq!(buffer.text(), "abcD");
-
-        buffer.edit([(4..4, "F")], None, cx);
-        assert_eq!(buffer.text(), "abcDF");
-        buffer
-    });
-    assert_eq!(buffer1.read(cx).text(), "abcDF");
-
-    let state = buffer1.read(cx).to_proto();
-    let ops = cx
-        .background()
-        .block(buffer1.read(cx).serialize_ops(None, cx));
-    let buffer2 = cx.add_model(|cx| {
-        let mut buffer = Buffer::from_proto(1, state, None).unwrap();
-        buffer
-            .apply_ops(
-                ops.into_iter()
-                    .map(|op| proto::deserialize_operation(op).unwrap()),
-                cx,
-            )
-            .unwrap();
-        buffer
-    });
-    assert_eq!(buffer2.read(cx).text(), "abcDF");
-}
-
-#[gpui::test(iterations = 100)]
-fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
-    let min_peers = env::var("MIN_PEERS")
-        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
-        .unwrap_or(1);
-    let max_peers = env::var("MAX_PEERS")
-        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
-        .unwrap_or(5);
-    let operations = env::var("OPERATIONS")
-        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-        .unwrap_or(10);
-
-    let base_text_len = rng.gen_range(0..10);
-    let base_text = RandomCharIter::new(&mut rng)
-        .take(base_text_len)
-        .collect::<String>();
-    let mut replica_ids = Vec::new();
-    let mut buffers = Vec::new();
-    let network = Rc::new(RefCell::new(Network::new(rng.clone())));
-    let base_buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, base_text.as_str()));
-
-    for i in 0..rng.gen_range(min_peers..=max_peers) {
-        let buffer = cx.add_model(|cx| {
-            let state = base_buffer.read(cx).to_proto();
-            let ops = cx
-                .background()
-                .block(base_buffer.read(cx).serialize_ops(None, cx));
-            let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
-            buffer
-                .apply_ops(
-                    ops.into_iter()
-                        .map(|op| proto::deserialize_operation(op).unwrap()),
-                    cx,
-                )
-                .unwrap();
-            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
-            let network = network.clone();
-            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
-                if let Event::Operation(op) = event {
-                    network
-                        .borrow_mut()
-                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
-                }
-            })
-            .detach();
-            buffer
-        });
-        buffers.push(buffer);
-        replica_ids.push(i as ReplicaId);
-        network.borrow_mut().add_peer(i as ReplicaId);
-        log::info!("Adding initial peer with replica id {}", i);
-    }
-
-    log::info!("initial text: {:?}", base_text);
-
-    let mut now = Instant::now();
-    let mut mutation_count = operations;
-    let mut next_diagnostic_id = 0;
-    let mut active_selections = BTreeMap::default();
-    loop {
-        let replica_index = rng.gen_range(0..replica_ids.len());
-        let replica_id = replica_ids[replica_index];
-        let buffer = &mut buffers[replica_index];
-        let mut new_buffer = None;
-        match rng.gen_range(0..100) {
-            0..=29 if mutation_count != 0 => {
-                buffer.update(cx, |buffer, cx| {
-                    buffer.start_transaction_at(now);
-                    buffer.randomly_edit(&mut rng, 5, cx);
-                    buffer.end_transaction_at(now, cx);
-                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
-                });
-                mutation_count -= 1;
-            }
-            30..=39 if mutation_count != 0 => {
-                buffer.update(cx, |buffer, cx| {
-                    if rng.gen_bool(0.2) {
-                        log::info!("peer {} clearing active selections", replica_id);
-                        active_selections.remove(&replica_id);
-                        buffer.remove_active_selections(cx);
-                    } else {
-                        let mut selections = Vec::new();
-                        for id in 0..rng.gen_range(1..=5) {
-                            let range = buffer.random_byte_range(0, &mut rng);
-                            selections.push(Selection {
-                                id,
-                                start: buffer.anchor_before(range.start),
-                                end: buffer.anchor_before(range.end),
-                                reversed: false,
-                                goal: SelectionGoal::None,
-                            });
-                        }
-                        let selections: Arc<[Selection<Anchor>]> = selections.into();
-                        log::info!(
-                            "peer {} setting active selections: {:?}",
-                            replica_id,
-                            selections
-                        );
-                        active_selections.insert(replica_id, selections.clone());
-                        buffer.set_active_selections(selections, false, Default::default(), cx);
-                    }
-                });
-                mutation_count -= 1;
-            }
-            40..=49 if mutation_count != 0 && replica_id == 0 => {
-                let entry_count = rng.gen_range(1..=5);
-                buffer.update(cx, |buffer, cx| {
-                    let diagnostics = DiagnosticSet::new(
-                        (0..entry_count).map(|_| {
-                            let range = buffer.random_byte_range(0, &mut rng);
-                            let range = range.to_point_utf16(buffer);
-                            let range = range.start..range.end;
-                            DiagnosticEntry {
-                                range,
-                                diagnostic: Diagnostic {
-                                    message: post_inc(&mut next_diagnostic_id).to_string(),
-                                    ..Default::default()
-                                },
-                            }
-                        }),
-                        buffer,
-                    );
-                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
-                    buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
-                });
-                mutation_count -= 1;
-            }
-            50..=59 if replica_ids.len() < max_peers => {
-                let old_buffer_state = buffer.read(cx).to_proto();
-                let old_buffer_ops = cx
-                    .background()
-                    .block(buffer.read(cx).serialize_ops(None, cx));
-                let new_replica_id = (0..=replica_ids.len() as ReplicaId)
-                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
-                    .choose(&mut rng)
-                    .unwrap();
-                log::info!(
-                    "Adding new replica {} (replicating from {})",
-                    new_replica_id,
-                    replica_id
-                );
-                new_buffer = Some(cx.add_model(|cx| {
-                    let mut new_buffer =
-                        Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
-                    new_buffer
-                        .apply_ops(
-                            old_buffer_ops
-                                .into_iter()
-                                .map(|op| deserialize_operation(op).unwrap()),
-                            cx,
-                        )
-                        .unwrap();
-                    log::info!(
-                        "New replica {} text: {:?}",
-                        new_buffer.replica_id(),
-                        new_buffer.text()
-                    );
-                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
-                    let network = network.clone();
-                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
-                        if let Event::Operation(op) = event {
-                            network.borrow_mut().broadcast(
-                                buffer.replica_id(),
-                                vec![proto::serialize_operation(op)],
-                            );
-                        }
-                    })
-                    .detach();
-                    new_buffer
-                }));
-                network.borrow_mut().replicate(replica_id, new_replica_id);
-
-                if new_replica_id as usize == replica_ids.len() {
-                    replica_ids.push(new_replica_id);
-                } else {
-                    let new_buffer = new_buffer.take().unwrap();
-                    while network.borrow().has_unreceived(new_replica_id) {
-                        let ops = network
-                            .borrow_mut()
-                            .receive(new_replica_id)
-                            .into_iter()
-                            .map(|op| proto::deserialize_operation(op).unwrap());
-                        if ops.len() > 0 {
-                            log::info!(
-                                "peer {} (version: {:?}) applying {} ops from the network. {:?}",
-                                new_replica_id,
-                                buffer.read(cx).version(),
-                                ops.len(),
-                                ops
-                            );
-                            new_buffer.update(cx, |new_buffer, cx| {
-                                new_buffer.apply_ops(ops, cx).unwrap();
-                            });
-                        }
-                    }
-                    buffers[new_replica_id as usize] = new_buffer;
-                }
-            }
-            60..=69 if mutation_count != 0 => {
-                buffer.update(cx, |buffer, cx| {
-                    buffer.randomly_undo_redo(&mut rng, cx);
-                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
-                });
-                mutation_count -= 1;
-            }
-            _ if network.borrow().has_unreceived(replica_id) => {
-                let ops = network
-                    .borrow_mut()
-                    .receive(replica_id)
-                    .into_iter()
-                    .map(|op| proto::deserialize_operation(op).unwrap());
-                if ops.len() > 0 {
-                    log::info!(
-                        "peer {} (version: {:?}) applying {} ops from the network. {:?}",
-                        replica_id,
-                        buffer.read(cx).version(),
-                        ops.len(),
-                        ops
-                    );
-                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
-                }
-            }
-            _ => {}
-        }
-
-        now += Duration::from_millis(rng.gen_range(0..=200));
-        buffers.extend(new_buffer);
-
-        for buffer in &buffers {
-            buffer.read(cx).check_invariants();
-        }
-
-        if mutation_count == 0 && network.borrow().is_idle() {
-            break;
-        }
-    }
-
-    let first_buffer = buffers[0].read(cx).snapshot();
-    for buffer in &buffers[1..] {
-        let buffer = buffer.read(cx).snapshot();
-        assert_eq!(
-            buffer.version(),
-            first_buffer.version(),
-            "Replica {} version != Replica 0 version",
-            buffer.replica_id()
-        );
-        assert_eq!(
-            buffer.text(),
-            first_buffer.text(),
-            "Replica {} text != Replica 0 text",
-            buffer.replica_id()
-        );
-        assert_eq!(
-            buffer
-                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
-                .collect::<Vec<_>>(),
-            first_buffer
-                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
-                .collect::<Vec<_>>(),
-            "Replica {} diagnostics != Replica 0 diagnostics",
-            buffer.replica_id()
-        );
-    }
-
-    for buffer in &buffers {
-        let buffer = buffer.read(cx).snapshot();
-        let actual_remote_selections = buffer
-            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
-            .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
-            .collect::<Vec<_>>();
-        let expected_remote_selections = active_selections
-            .iter()
-            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
-            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
-            .collect::<Vec<_>>();
-        assert_eq!(
-            actual_remote_selections,
-            expected_remote_selections,
-            "Replica {} remote selections != expected selections",
-            buffer.replica_id()
-        );
-    }
-}
-
-#[test]
-fn test_contiguous_ranges() {
-    assert_eq!(
-        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
-        &[1..4, 5..7, 9..13]
-    );
-
-    // Respects the `max_len` parameter
-    assert_eq!(
-        contiguous_ranges(
-            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
-            3
-        )
-        .collect::<Vec<_>>(),
-        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
-    );
-}
-
-#[gpui::test(iterations = 500)]
-fn test_trailing_whitespace_ranges(mut rng: StdRng) {
-    // Generate a random multi-line string containing
-    // some lines with trailing whitespace.
-    let mut text = String::new();
-    for _ in 0..rng.gen_range(0..16) {
-        for _ in 0..rng.gen_range(0..36) {
-            text.push(match rng.gen_range(0..10) {
-                0..=1 => ' ',
-                3 => '\t',
-                _ => rng.gen_range('a'..'z'),
-            });
-        }
-        text.push('\n');
-    }
-
-    match rng.gen_range(0..10) {
-        // sometimes remove the last newline
-        0..=1 => drop(text.pop()), //
-
-        // sometimes add extra newlines
-        2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
-        _ => {}
-    }
-
-    let rope = Rope::from(text.as_str());
-    let actual_ranges = trailing_whitespace_ranges(&rope);
-    let expected_ranges = TRAILING_WHITESPACE_REGEX
-        .find_iter(&text)
-        .map(|m| m.range())
-        .collect::<Vec<_>>();
-    assert_eq!(
-        actual_ranges,
-        expected_ranges,
-        "wrong ranges for text lines:\n{:?}",
-        text.split("\n").collect::<Vec<_>>()
-    );
-}
-
-fn ruby_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "Ruby".into(),
-            path_suffixes: vec!["rb".to_string()],
-            line_comment: Some("# ".into()),
-            ..Default::default()
-        },
-        Some(tree_sitter_ruby::language()),
-    )
-    .with_indents_query(
-        r#"
-            (class "end" @end) @indent
-            (method "end" @end) @indent
-            (rescue) @outdent
-            (then) @indent
-        "#,
-    )
-    .unwrap()
-}
-
-fn html_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "HTML".into(),
-            block_comment: Some(("<!--".into(), "-->".into())),
-            ..Default::default()
-        },
-        Some(tree_sitter_html::language()),
-    )
-    .with_indents_query(
-        "
-        (element
-          (start_tag) @start
-          (end_tag)? @end) @indent
-        ",
-    )
-    .unwrap()
-    .with_injection_query(
-        r#"
-        (script_element
-            (raw_text) @content
-            (#set! "language" "javascript"))
-        "#,
-    )
-    .unwrap()
-}
-
-fn erb_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "ERB".into(),
-            path_suffixes: vec!["erb".to_string()],
-            block_comment: Some(("<%#".into(), "%>".into())),
-            ..Default::default()
-        },
-        Some(tree_sitter_embedded_template::language()),
-    )
-    .with_injection_query(
-        r#"
-            (
-                (code) @content
-                (#set! "language" "ruby")
-                (#set! "combined")
-            )
-
-            (
-                (content) @content
-                (#set! "language" "html")
-                (#set! "combined")
-            )
-        "#,
-    )
-    .unwrap()
-}
-
-fn rust_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    )
-    .with_indents_query(
-        r#"
-        (call_expression) @indent
-        (field_expression) @indent
-        (_ "(" ")" @end) @indent
-        (_ "{" "}" @end) @indent
-        "#,
-    )
-    .unwrap()
-    .with_brackets_query(
-        r#"
-        ("{" @open "}" @close)
-        "#,
-    )
-    .unwrap()
-    .with_outline_query(
-        r#"
-        (struct_item
-            "struct" @context
-            name: (_) @name) @item
-        (enum_item
-            "enum" @context
-            name: (_) @name) @item
-        (enum_variant
-            name: (_) @name) @item
-        (field_declaration
-            name: (_) @name) @item
-        (impl_item
-            "impl" @context
-            trait: (_)? @name
-            "for"? @context
-            type: (_) @name) @item
-        (function_item
-            "fn" @context
-            name: (_) @name) @item
-        (mod_item
-            "mod" @context
-            name: (_) @name) @item
-        "#,
-    )
-    .unwrap()
-}
-
-fn json_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "Json".into(),
-            path_suffixes: vec!["js".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_json::language()),
-    )
-}
-
-fn javascript_lang() -> Language {
-    Language::new(
-        LanguageConfig {
-            name: "JavaScript".into(),
-            ..Default::default()
-        },
-        Some(tree_sitter_typescript::language_tsx()),
-    )
-    .with_brackets_query(
-        r#"
-        ("{" @open "}" @close)
-        ("(" @open ")" @close)
-        "#,
-    )
-    .unwrap()
-    .with_indents_query(
-        r#"
-        (object "}" @end) @indent
-        "#,
-    )
-    .unwrap()
-}
-
-fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
-    buffer.read_with(cx, |buffer, _| {
-        let snapshot = buffer.snapshot();
-        let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
-        layers[0].node().to_sexp()
-    })
-}
-
-// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
-fn assert_bracket_pairs(
-    selection_text: &'static str,
-    bracket_pair_texts: Vec<&'static str>,
-    language: Language,
-    cx: &mut AppContext,
-) {
-    let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
-    let buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, expected_text.clone())
-            .with_language(Arc::new(language), cx)
-    });
-    let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
-
-    let selection_range = selection_ranges[0].clone();
-
-    let bracket_pairs = bracket_pair_texts
-        .into_iter()
-        .map(|pair_text| {
-            let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
-            assert_eq!(bracket_text, expected_text);
-            (ranges[0].clone(), ranges[1].clone())
-        })
-        .collect::<Vec<_>>();
-
-    assert_set_eq!(
-        buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
-        bracket_pairs
-    );
-}
-
-fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
-    cx.set_global(SettingsStore::test(cx));
-    crate::init(cx);
-    cx.update_global::<SettingsStore, _, _>(|settings, cx| {
-        settings.update_user_settings::<AllLanguageSettings>(cx, f);
-    });
-}
+// use crate::language_settings::{
+//     AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
+// };
+
+// use super::*;
+// use clock::ReplicaId;
+// use collections::BTreeMap;
+// use gpui2::{AppContext, Handle};
+// use indoc::indoc;
+// use proto::deserialize_operation;
+// use rand::prelude::*;
+// use regex::RegexBuilder;
+// use settings::SettingsStore;
+// use std::{
+//     cell::RefCell,
+//     env,
+//     ops::Range,
+//     rc::Rc,
+//     time::{Duration, Instant},
+// };
+// use text::network::Network;
+// use text::LineEnding;
+// use unindent::Unindent as _;
+// use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
+
+// lazy_static! {
+//     static ref TRAILING_WHITESPACE_REGEX: Regex = RegexBuilder::new("[ \t]+$")
+//         .multi_line(true)
+//         .build()
+//         .unwrap();
+// }
+
+// #[cfg(test)]
+// #[ctor::ctor]
+// fn init_logger() {
+//     if std::env::var("RUST_LOG").is_ok() {
+//         env_logger::init();
+//     }
+// }
+
+// #[gpui::test]
+// fn test_line_endings(cx: &mut gpui::AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(0, cx.model_id() as u64, "one\r\ntwo\rthree")
+//             .with_language(Arc::new(rust_lang()), cx);
+//         assert_eq!(buffer.text(), "one\ntwo\nthree");
+//         assert_eq!(buffer.line_ending(), LineEnding::Windows);
+
+//         buffer.check_invariants();
+//         buffer.edit(
+//             [(buffer.len()..buffer.len(), "\r\nfour")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         buffer.edit([(0..0, "zero\r\n")], None, cx);
+//         assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
+//         assert_eq!(buffer.line_ending(), LineEnding::Windows);
+//         buffer.check_invariants();
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_select_language() {
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     )));
+//     registry.add(Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "Make".into(),
+//             path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     )));
+
+//     // matching file extension
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/lib.rs", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         Some("Rust".into())
+//     );
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/lib.mk", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         Some("Make".into())
+//     );
+
+//     // matching filename
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/Makefile", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         Some("Make".into())
+//     );
+
+//     // matching suffix that is not the full file extension or filename
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/cars", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         None
+//     );
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/a.cars", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         None
+//     );
+//     assert_eq!(
+//         registry
+//             .language_for_file("zed/sumk", None)
+//             .now_or_never()
+//             .and_then(|l| Some(l.ok()?.name())),
+//         None
+//     );
+// }
+
+// #[gpui::test]
+// fn test_edit_events(cx: &mut gpui::AppContext) {
+//     let mut now = Instant::now();
+//     let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
+//     let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
+
+//     let buffer1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abcdef"));
+//     let buffer2 = cx.add_model(|cx| Buffer::new(1, cx.model_id() as u64, "abcdef"));
+//     let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
+//     buffer1.update(cx, {
+//         let buffer1_ops = buffer1_ops.clone();
+//         |buffer, cx| {
+//             let buffer_1_events = buffer_1_events.clone();
+//             cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
+//                 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
+//                 event => buffer_1_events.borrow_mut().push(event),
+//             })
+//             .detach();
+//             let buffer_2_events = buffer_2_events.clone();
+//             cx.subscribe(&buffer2, move |_, _, event, _| {
+//                 buffer_2_events.borrow_mut().push(event.clone())
+//             })
+//             .detach();
+
+//             // An edit emits an edited event, followed by a dirty changed event,
+//             // since the buffer was previously in a clean state.
+//             buffer.edit([(2..4, "XYZ")], None, cx);
+
+//             // An empty transaction does not emit any events.
+//             buffer.start_transaction();
+//             buffer.end_transaction(cx);
+
+//             // A transaction containing two edits emits one edited event.
+//             now += Duration::from_secs(1);
+//             buffer.start_transaction_at(now);
+//             buffer.edit([(5..5, "u")], None, cx);
+//             buffer.edit([(6..6, "w")], None, cx);
+//             buffer.end_transaction_at(now, cx);
+
+//             // Undoing a transaction emits one edited event.
+//             buffer.undo(cx);
+//         }
+//     });
+
+//     // Incorporating a set of remote ops emits a single edited event,
+//     // followed by a dirty changed event.
+//     buffer2.update(cx, |buffer, cx| {
+//         buffer
+//             .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
+//             .unwrap();
+//     });
+//     assert_eq!(
+//         mem::take(&mut *buffer_1_events.borrow_mut()),
+//         vec![
+//             Event::Edited,
+//             Event::DirtyChanged,
+//             Event::Edited,
+//             Event::Edited,
+//         ]
+//     );
+//     assert_eq!(
+//         mem::take(&mut *buffer_2_events.borrow_mut()),
+//         vec![Event::Edited, Event::DirtyChanged]
+//     );
+
+//     buffer1.update(cx, |buffer, cx| {
+//         // Undoing the first transaction emits edited event, followed by a
+//         // dirty changed event, since the buffer is again in a clean state.
+//         buffer.undo(cx);
+//     });
+//     // Incorporating the remote ops again emits a single edited event,
+//     // followed by a dirty changed event.
+//     buffer2.update(cx, |buffer, cx| {
+//         buffer
+//             .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
+//             .unwrap();
+//     });
+//     assert_eq!(
+//         mem::take(&mut *buffer_1_events.borrow_mut()),
+//         vec![Event::Edited, Event::DirtyChanged,]
+//     );
+//     assert_eq!(
+//         mem::take(&mut *buffer_2_events.borrow_mut()),
+//         vec![Event::Edited, Event::DirtyChanged]
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
+//     let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
+//     let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
+
+//     let text = "a\nccc\ndddd\nffffff\n";
+//     let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
+//     buffer.update(cx, |buffer, cx| {
+//         buffer.apply_diff(diff, cx).unwrap();
+//         assert_eq!(buffer.text(), text);
+//         assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
+//     });
+
+//     let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
+//     let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
+//     buffer.update(cx, |buffer, cx| {
+//         buffer.apply_diff(diff, cx).unwrap();
+//         assert_eq!(buffer.text(), text);
+//         assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
+//     });
+// }
+
+// #[gpui::test(iterations = 10)]
+// async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
+//     let text = [
+//         "zero",     //
+//         "one  ",    // 2 trailing spaces
+//         "two",      //
+//         "three   ", // 3 trailing spaces
+//         "four",     //
+//         "five    ", // 4 trailing spaces
+//     ]
+//     .join("\n");
+
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
+
+//     // Spawn a task to format the buffer's whitespace.
+//     // Pause so that the foratting task starts running.
+//     let format = buffer.read_with(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
+//     smol::future::yield_now().await;
+
+//     // Edit the buffer while the normalization task is running.
+//     let version_before_edit = buffer.read_with(cx, |buffer, _| buffer.version());
+//     buffer.update(cx, |buffer, cx| {
+//         buffer.edit(
+//             [
+//                 (Point::new(0, 1)..Point::new(0, 1), "EE"),
+//                 (Point::new(3, 5)..Point::new(3, 5), "EEE"),
+//             ],
+//             None,
+//             cx,
+//         );
+//     });
+
+//     let format_diff = format.await;
+//     buffer.update(cx, |buffer, cx| {
+//         let version_before_format = format_diff.base_version.clone();
+//         buffer.apply_diff(format_diff, cx);
+
+//         // The outcome depends on the order of concurrent taks.
+//         //
+//         // If the edit occurred while searching for trailing whitespace ranges,
+//         // then the trailing whitespace region touched by the edit is left intact.
+//         if version_before_format == version_before_edit {
+//             assert_eq!(
+//                 buffer.text(),
+//                 [
+//                     "zEEero",      //
+//                     "one",         //
+//                     "two",         //
+//                     "threeEEE   ", //
+//                     "four",        //
+//                     "five",        //
+//                 ]
+//                 .join("\n")
+//             );
+//         }
+//         // Otherwise, all trailing whitespace is removed.
+//         else {
+//             assert_eq!(
+//                 buffer.text(),
+//                 [
+//                     "zEEero",   //
+//                     "one",      //
+//                     "two",      //
+//                     "threeEEE", //
+//                     "four",     //
+//                     "five",     //
+//                 ]
+//                 .join("\n")
+//             );
+//         }
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_reparse(cx: &mut gpui::TestAppContext) {
+//     let text = "fn a() {}";
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
+//     });
+
+//     // Wait for the initial text to parse
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         concat!(
+//             "(source_file (function_item name: (identifier) ",
+//             "parameters: (parameters) ",
+//             "body: (block)))"
+//         )
+//     );
+
+//     buffer.update(cx, |buffer, _| {
+//         buffer.set_sync_parse_timeout(Duration::ZERO)
+//     });
+
+//     // Perform some edits (add parameter and variable reference)
+//     // Parsing doesn't begin until the transaction is complete
+//     buffer.update(cx, |buf, cx| {
+//         buf.start_transaction();
+
+//         let offset = buf.text().find(')').unwrap();
+//         buf.edit([(offset..offset, "b: C")], None, cx);
+//         assert!(!buf.is_parsing());
+
+//         let offset = buf.text().find('}').unwrap();
+//         buf.edit([(offset..offset, " d; ")], None, cx);
+//         assert!(!buf.is_parsing());
+
+//         buf.end_transaction(cx);
+//         assert_eq!(buf.text(), "fn a(b: C) { d; }");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         concat!(
+//             "(source_file (function_item name: (identifier) ",
+//             "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
+//             "body: (block (expression_statement (identifier)))))"
+//         )
+//     );
+
+//     // Perform a series of edits without waiting for the current parse to complete:
+//     // * turn identifier into a field expression
+//     // * turn field expression into a method call
+//     // * add a turbofish to the method call
+//     buffer.update(cx, |buf, cx| {
+//         let offset = buf.text().find(';').unwrap();
+//         buf.edit([(offset..offset, ".e")], None, cx);
+//         assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.update(cx, |buf, cx| {
+//         let offset = buf.text().find(';').unwrap();
+//         buf.edit([(offset..offset, "(f)")], None, cx);
+//         assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.update(cx, |buf, cx| {
+//         let offset = buf.text().find("(f)").unwrap();
+//         buf.edit([(offset..offset, "::<G>")], None, cx);
+//         assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         concat!(
+//             "(source_file (function_item name: (identifier) ",
+//             "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
+//             "body: (block (expression_statement (call_expression ",
+//             "function: (generic_function ",
+//             "function: (field_expression value: (identifier) field: (field_identifier)) ",
+//             "type_arguments: (type_arguments (type_identifier))) ",
+//             "arguments: (arguments (identifier)))))))",
+//         )
+//     );
+
+//     buffer.update(cx, |buf, cx| {
+//         buf.undo(cx);
+//         buf.undo(cx);
+//         buf.undo(cx);
+//         buf.undo(cx);
+//         assert_eq!(buf.text(), "fn a() {}");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         concat!(
+//             "(source_file (function_item name: (identifier) ",
+//             "parameters: (parameters) ",
+//             "body: (block)))"
+//         )
+//     );
+
+//     buffer.update(cx, |buf, cx| {
+//         buf.redo(cx);
+//         buf.redo(cx);
+//         buf.redo(cx);
+//         buf.redo(cx);
+//         assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
+//         assert!(buf.is_parsing());
+//     });
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         concat!(
+//             "(source_file (function_item name: (identifier) ",
+//             "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
+//             "body: (block (expression_statement (call_expression ",
+//             "function: (generic_function ",
+//             "function: (field_expression value: (identifier) field: (field_identifier)) ",
+//             "type_arguments: (type_arguments (type_identifier))) ",
+//             "arguments: (arguments (identifier)))))))",
+//         )
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
+//     let buffer = cx.add_model(|cx| {
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, "{}").with_language(Arc::new(rust_lang()), cx);
+//         buffer.set_sync_parse_timeout(Duration::ZERO);
+//         buffer
+//     });
+
+//     // Wait for the initial text to parse
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(
+//         get_tree_sexp(&buffer, cx),
+//         "(source_file (expression_statement (block)))"
+//     );
+
+//     buffer.update(cx, |buffer, cx| {
+//         buffer.set_language(Some(Arc::new(json_lang())), cx)
+//     });
+//     buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
+//     assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
+// }
+
+// #[gpui::test]
+// async fn test_outline(cx: &mut gpui::TestAppContext) {
+//     let text = r#"
+//         struct Person {
+//             name: String,
+//             age: usize,
+//         }
+
+//         mod module {
+//             enum LoginState {
+//                 LoggedOut,
+//                 LoggingOn,
+//                 LoggedIn {
+//                     person: Person,
+//                     time: Instant,
+//                 }
+//             }
+//         }
+
+//         impl Eq for Person {}
+
+//         impl Drop for Person {
+//             fn drop(&mut self) {
+//                 println!("bye");
+//             }
+//         }
+//     "#
+//     .unindent();
+
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
+//     });
+//     let outline = buffer
+//         .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
+//         .unwrap();
+
+//     assert_eq!(
+//         outline
+//             .items
+//             .iter()
+//             .map(|item| (item.text.as_str(), item.depth))
+//             .collect::<Vec<_>>(),
+//         &[
+//             ("struct Person", 0),
+//             ("name", 1),
+//             ("age", 1),
+//             ("mod module", 0),
+//             ("enum LoginState", 1),
+//             ("LoggedOut", 2),
+//             ("LoggingOn", 2),
+//             ("LoggedIn", 2),
+//             ("person", 3),
+//             ("time", 3),
+//             ("impl Eq for Person", 0),
+//             ("impl Drop for Person", 0),
+//             ("fn drop", 1),
+//         ]
+//     );
+
+//     // Without space, we only match on names
+//     assert_eq!(
+//         search(&outline, "oon", cx).await,
+//         &[
+//             ("mod module", vec![]),                    // included as the parent of a match
+//             ("enum LoginState", vec![]),               // included as the parent of a match
+//             ("LoggingOn", vec![1, 7, 8]),              // matches
+//             ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
+//         ]
+//     );
+
+//     assert_eq!(
+//         search(&outline, "dp p", cx).await,
+//         &[
+//             ("impl Drop for Person", vec![5, 8, 9, 14]),
+//             ("fn drop", vec![]),
+//         ]
+//     );
+//     assert_eq!(
+//         search(&outline, "dpn", cx).await,
+//         &[("impl Drop for Person", vec![5, 14, 19])]
+//     );
+//     assert_eq!(
+//         search(&outline, "impl ", cx).await,
+//         &[
+//             ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
+//             ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
+//             ("fn drop", vec![]),
+//         ]
+//     );
+
+//     async fn search<'a>(
+//         outline: &'a Outline<Anchor>,
+//         query: &'a str,
+//         cx: &'a gpui::TestAppContext,
+//     ) -> Vec<(&'a str, Vec<usize>)> {
+//         let matches = cx
+//             .read(|cx| outline.search(query, cx.background().clone()))
+//             .await;
+//         matches
+//             .into_iter()
+//             .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
+//             .collect::<Vec<_>>()
+//     }
+// }
+
+// #[gpui::test]
+// async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
+//     let text = r#"
+//         impl A for B<
+//             C
+//         > {
+//         };
+//     "#
+//     .unindent();
+
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
+//     });
+//     let outline = buffer
+//         .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
+//         .unwrap();
+
+//     assert_eq!(
+//         outline
+//             .items
+//             .iter()
+//             .map(|item| (item.text.as_str(), item.depth))
+//             .collect::<Vec<_>>(),
+//         &[("impl A for B<", 0)]
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
+//     let language = javascript_lang()
+//         .with_outline_query(
+//             r#"
+//             (function_declaration
+//                 "function" @context
+//                 name: (_) @name
+//                 parameters: (formal_parameters
+//                     "(" @context.extra
+//                     ")" @context.extra)) @item
+//             "#,
+//         )
+//         .unwrap();
+
+//     let text = r#"
+//         function a() {}
+//         function b(c) {}
+//     "#
+//     .unindent();
+
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(language), cx)
+//     });
+//     let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
+
+//     // extra context nodes are included in the outline.
+//     let outline = snapshot.outline(None).unwrap();
+//     assert_eq!(
+//         outline
+//             .items
+//             .iter()
+//             .map(|item| (item.text.as_str(), item.depth))
+//             .collect::<Vec<_>>(),
+//         &[("function a()", 0), ("function b( )", 0),]
+//     );
+
+//     // extra context nodes do not appear in breadcrumbs.
+//     let symbols = snapshot.symbols_containing(3, None).unwrap();
+//     assert_eq!(
+//         symbols
+//             .iter()
+//             .map(|item| (item.text.as_str(), item.depth))
+//             .collect::<Vec<_>>(),
+//         &[("function a", 0)]
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
+//     let text = r#"
+//         impl Person {
+//             fn one() {
+//                 1
+//             }
+
+//             fn two() {
+//                 2
+//             }fn three() {
+//                 3
+//             }
+//         }
+//     "#
+//     .unindent();
+
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx)
+//     });
+//     let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
+
+//     // point is at the start of an item
+//     assert_eq!(
+//         symbols_containing(Point::new(1, 4), &snapshot),
+//         vec![
+//             (
+//                 "impl Person".to_string(),
+//                 Point::new(0, 0)..Point::new(10, 1)
+//             ),
+//             ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+//         ]
+//     );
+
+//     // point is in the middle of an item
+//     assert_eq!(
+//         symbols_containing(Point::new(2, 8), &snapshot),
+//         vec![
+//             (
+//                 "impl Person".to_string(),
+//                 Point::new(0, 0)..Point::new(10, 1)
+//             ),
+//             ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+//         ]
+//     );
+
+//     // point is at the end of an item
+//     assert_eq!(
+//         symbols_containing(Point::new(3, 5), &snapshot),
+//         vec![
+//             (
+//                 "impl Person".to_string(),
+//                 Point::new(0, 0)..Point::new(10, 1)
+//             ),
+//             ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
+//         ]
+//     );
+
+//     // point is in between two adjacent items
+//     assert_eq!(
+//         symbols_containing(Point::new(7, 5), &snapshot),
+//         vec![
+//             (
+//                 "impl Person".to_string(),
+//                 Point::new(0, 0)..Point::new(10, 1)
+//             ),
+//             ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
+//         ]
+//     );
+
+//     fn symbols_containing(
+//         position: Point,
+//         snapshot: &BufferSnapshot,
+//     ) -> Vec<(String, Range<Point>)> {
+//         snapshot
+//             .symbols_containing(position, None)
+//             .unwrap()
+//             .into_iter()
+//             .map(|item| {
+//                 (
+//                     item.text,
+//                     item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
+//                 )
+//             })
+//             .collect()
+//     }
+// }
+
+// #[gpui::test]
+// fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
+//     let mut assert = |selection_text, range_markers| {
+//         assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
+//     };
+
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 moˇd y {
+
+//                 }
+//             }
+//             let foo = 1;"},
+//         vec![indoc! {"
+//             mod x «{»
+//                 mod y {
+
+//                 }
+//             «}»
+//             let foo = 1;"}],
+//     );
+
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 mod y ˇ{
+
+//                 }
+//             }
+//             let foo = 1;"},
+//         vec![
+//             indoc! {"
+//                 mod x «{»
+//                     mod y {
+
+//                     }
+//                 «}»
+//                 let foo = 1;"},
+//             indoc! {"
+//                 mod x {
+//                     mod y «{»
+
+//                     «}»
+//                 }
+//                 let foo = 1;"},
+//         ],
+//     );
+
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 mod y {
+
+//                 }ˇ
+//             }
+//             let foo = 1;"},
+//         vec![
+//             indoc! {"
+//                 mod x «{»
+//                     mod y {
+
+//                     }
+//                 «}»
+//                 let foo = 1;"},
+//             indoc! {"
+//                 mod x {
+//                     mod y «{»
+
+//                     «}»
+//                 }
+//                 let foo = 1;"},
+//         ],
+//     );
+
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 mod y {
+
+//                 }
+//             ˇ}
+//             let foo = 1;"},
+//         vec![indoc! {"
+//             mod x «{»
+//                 mod y {
+
+//                 }
+//             «}»
+//             let foo = 1;"}],
+//     );
+
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 mod y {
+
+//                 }
+//             }
+//             let fˇoo = 1;"},
+//         vec![],
+//     );
+
+//     // Regression test: avoid crash when querying at the end of the buffer.
+//     assert(
+//         indoc! {"
+//             mod x {
+//                 mod y {
+
+//                 }
+//             }
+//             let foo = 1;ˇ"},
+//         vec![],
+//     );
+// }
+
+// #[gpui::test]
+// fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
+//     let mut assert = |selection_text, bracket_pair_texts| {
+//         assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
+//     };
+
+//     assert(
+//         indoc! {"
+//         for (const a in b)ˇ {
+//             // a comment that's longer than the for-loop header
+//         }"},
+//         vec![indoc! {"
+//         for «(»const a in b«)» {
+//             // a comment that's longer than the for-loop header
+//         }"}],
+//     );
+
+//     // Regression test: even though the parent node of the parentheses (the for loop) does
+//     // intersect the given range, the parentheses themselves do not contain the range, so
+//     // they should not be returned. Only the curly braces contain the range.
+//     assert(
+//         indoc! {"
+//         for (const a in b) {ˇ
+//             // a comment that's longer than the for-loop header
+//         }"},
+//         vec![indoc! {"
+//         for (const a in b) «{»
+//             // a comment that's longer than the for-loop header
+//         «}»"}],
+//     );
+// }
+
+// #[gpui::test]
+// fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
+//     cx.add_model(|cx| {
+//         let text = "fn a() { b(|c| {}) }";
+//         let buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+//         let snapshot = buffer.snapshot();
+
+//         assert_eq!(
+//             snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
+//             Some(range_of(text, "|"))
+//         );
+//         assert_eq!(
+//             snapshot.range_for_syntax_ancestor(range_of(text, "|")),
+//             Some(range_of(text, "|c|"))
+//         );
+//         assert_eq!(
+//             snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
+//             Some(range_of(text, "|c| {}"))
+//         );
+//         assert_eq!(
+//             snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
+//             Some(range_of(text, "(|c| {})"))
+//         );
+
+//         buffer
+//     });
+
+//     fn empty_range_at(text: &str, part: &str) -> Range<usize> {
+//         let start = text.find(part).unwrap();
+//         start..start
+//     }
+
+//     fn range_of(text: &str, part: &str) -> Range<usize> {
+//         let start = text.find(part).unwrap();
+//         start..start + part.len()
+//     }
+// }
+
+// #[gpui::test]
+// fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = "fn a() {}";
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+
+//         buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
+//         assert_eq!(buffer.text(), "fn a() {\n    \n}");
+
+//         buffer.edit(
+//             [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
+
+//         // Create a field expression on a new line, causing that line
+//         // to be indented.
+//         buffer.edit(
+//             [(Point::new(2, 4)..Point::new(2, 4), ".c")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
+
+//         // Remove the dot so that the line is no longer a field expression,
+//         // causing the line to be outdented.
+//         buffer.edit(
+//             [(Point::new(2, 8)..Point::new(2, 9), "")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
+//     init_settings(cx, |settings| {
+//         settings.defaults.hard_tabs = Some(true);
+//     });
+
+//     cx.add_model(|cx| {
+//         let text = "fn a() {}";
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+
+//         buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
+//         assert_eq!(buffer.text(), "fn a() {\n\t\n}");
+
+//         buffer.edit(
+//             [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
+
+//         // Create a field expression on a new line, causing that line
+//         // to be indented.
+//         buffer.edit(
+//             [(Point::new(2, 1)..Point::new(2, 1), ".c")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
+
+//         // Remove the dot so that the line is no longer a field expression,
+//         // causing the line to be outdented.
+//         buffer.edit(
+//             [(Point::new(2, 2)..Point::new(2, 3), "")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(
+//             0,
+//             cx.model_id() as u64,
+//             "
+//             fn a() {
+//             c;
+//             d;
+//             }
+//             "
+//             .unindent(),
+//         )
+//         .with_language(Arc::new(rust_lang()), cx);
+
+//         // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
+//         // their indentation is not adjusted.
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//             c«()»;
+//             d«()»;
+//             }
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//             c();
+//             d();
+//             }
+//             "
+//             .unindent()
+//         );
+
+//         // When appending new content after these lines, the indentation is based on the
+//         // preceding lines' actual indentation.
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//             c«
+//             .f
+//             .g()»;
+//             d«
+//             .f
+//             .g()»;
+//             }
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//             c
+//                 .f
+//                 .g();
+//             d
+//                 .f
+//                 .g();
+//             }
+//             "
+//             .unindent()
+//         );
+//         buffer
+//     });
+
+//     cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(
+//             0,
+//             cx.model_id() as u64,
+//             "
+//             fn a() {
+//                 b();
+//                 |
+//             "
+//             .replace("|", "") // marker to preserve trailing whitespace
+//             .unindent(),
+//         )
+//         .with_language(Arc::new(rust_lang()), cx);
+
+//         // Insert a closing brace. It is outdented.
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//                 b();
+//                 «}»
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//                 b();
+//             }
+//             "
+//             .unindent()
+//         );
+
+//         // Manually edit the leading whitespace. The edit is preserved.
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//                 b();
+//             «    »}
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//                 b();
+//                 }
+//             "
+//             .unindent()
+//         );
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(
+//             0,
+//             cx.model_id() as u64,
+//             "
+//             fn a() {
+//                 i
+//             }
+//             "
+//             .unindent(),
+//         )
+//         .with_language(Arc::new(rust_lang()), cx);
+
+//         // Regression test: line does not get outdented due to syntax error
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//                 i«f let Some(x) = y»
+//             }
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//                 if let Some(x) = y
+//             }
+//             "
+//             .unindent()
+//         );
+
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a() {
+//                 if let Some(x) = y« {»
+//             }
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a() {
+//                 if let Some(x) = y {
+//             }
+//             "
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(
+//             0,
+//             cx.model_id() as u64,
+//             "
+//             fn a() {}
+//             "
+//             .unindent(),
+//         )
+//         .with_language(Arc::new(rust_lang()), cx);
+
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a(«
+//             b») {}
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             fn a(
+//                 b) {}
+//             "
+//             .unindent()
+//         );
+
+//         // The indentation suggestion changed because `@end` node (a close paren)
+//         // is now at the beginning of the line.
+//         buffer.edit_via_marked_text(
+//             &"
+//             fn a(
+//                 ˇ) {}
+//             "
+//             .unindent(),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//                 fn a(
+//                 ) {}
+//             "
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = "a\nb";
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+//         buffer.edit(
+//             [(0..1, "\n"), (2..3, "\n")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(buffer.text(), "\n\n\n");
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = "
+//             const a: usize = 1;
+//             fn b() {
+//                 if c {
+//                     let d = 2;
+//                 }
+//             }
+//         "
+//         .unindent();
+
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+//         buffer.edit(
+//             [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//                 const a: usize = 1;
+//                 fn b() {
+//                     if c {
+//                         e(
+//                             f()
+//                         );
+//                         let d = 2;
+//                     }
+//                 }
+//             "
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_block_mode(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = r#"
+//             fn a() {
+//                 b();
+//             }
+//         "#
+//         .unindent();
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+
+//         // When this text was copied, both of the quotation marks were at the same
+//         // indent level, but the indentation of the first line was not included in
+//         // the copied text. This information is retained in the
+//         // 'original_indent_columns' vector.
+//         let original_indent_columns = vec![4];
+//         let inserted_text = r#"
+//             "
+//                   c
+//                     d
+//                       e
+//                 "
+//         "#
+//         .unindent();
+
+//         // Insert the block at column zero. The entire block is indented
+//         // so that the first line matches the previous line's indentation.
+//         buffer.edit(
+//             [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
+//             Some(AutoindentMode::Block {
+//                 original_indent_columns: original_indent_columns.clone(),
+//             }),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             r#"
+//             fn a() {
+//                 b();
+//                 "
+//                   c
+//                     d
+//                       e
+//                 "
+//             }
+//             "#
+//             .unindent()
+//         );
+
+//         // Grouping is disabled in tests, so we need 2 undos
+//         buffer.undo(cx); // Undo the auto-indent
+//         buffer.undo(cx); // Undo the original edit
+
+//         // Insert the block at a deeper indent level. The entire block is outdented.
+//         buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
+//         buffer.edit(
+//             [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
+//             Some(AutoindentMode::Block {
+//                 original_indent_columns: original_indent_columns.clone(),
+//             }),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             r#"
+//             fn a() {
+//                 b();
+//                 "
+//                   c
+//                     d
+//                       e
+//                 "
+//             }
+//             "#
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = r#"
+//             fn a() {
+//                 if b() {
+
+//                 }
+//             }
+//         "#
+//         .unindent();
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(rust_lang()), cx);
+
+//         // The original indent columns are not known, so this text is
+//         // auto-indented in a block as if the first line was copied in
+//         // its entirety.
+//         let original_indent_columns = Vec::new();
+//         let inserted_text = "    c\n        .d()\n        .e();";
+
+//         // Insert the block at column zero. The entire block is indented
+//         // so that the first line matches the previous line's indentation.
+//         buffer.edit(
+//             [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
+//             Some(AutoindentMode::Block {
+//                 original_indent_columns: original_indent_columns.clone(),
+//             }),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             r#"
+//             fn a() {
+//                 if b() {
+//                     c
+//                         .d()
+//                         .e();
+//                 }
+//             }
+//             "#
+//             .unindent()
+//         );
+
+//         // Grouping is disabled in tests, so we need 2 undos
+//         buffer.undo(cx); // Undo the auto-indent
+//         buffer.undo(cx); // Undo the original edit
+
+//         // Insert the block at a deeper indent level. The entire block is outdented.
+//         buffer.edit(
+//             [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
+//             None,
+//             cx,
+//         );
+//         buffer.edit(
+//             [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
+//             Some(AutoindentMode::Block {
+//                 original_indent_columns: Vec::new(),
+//             }),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             r#"
+//             fn a() {
+//                 if b() {
+//                     c
+//                         .d()
+//                         .e();
+//                 }
+//             }
+//             "#
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = "
+//             * one
+//                 - a
+//                 - b
+//             * two
+//         "
+//         .unindent();
+
+//         let mut buffer = Buffer::new(0, cx.model_id() as u64, text).with_language(
+//             Arc::new(Language::new(
+//                 LanguageConfig {
+//                     name: "Markdown".into(),
+//                     auto_indent_using_last_non_empty_line: false,
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_json::language()),
+//             )),
+//             cx,
+//         );
+//         buffer.edit(
+//             [(Point::new(3, 0)..Point::new(3, 0), "\n")],
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//             * one
+//                 - a
+//                 - b
+
+//             * two
+//             "
+//             .unindent()
+//         );
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
+//     init_settings(cx, |settings| {
+//         settings.languages.extend([
+//             (
+//                 "HTML".into(),
+//                 LanguageSettingsContent {
+//                     tab_size: Some(2.try_into().unwrap()),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 "JavaScript".into(),
+//                 LanguageSettingsContent {
+//                     tab_size: Some(8.try_into().unwrap()),
+//                     ..Default::default()
+//                 },
+//             ),
+//         ])
+//     });
+
+//     let html_language = Arc::new(html_lang());
+
+//     let javascript_language = Arc::new(javascript_lang());
+
+//     let language_registry = Arc::new(LanguageRegistry::test());
+//     language_registry.add(html_language.clone());
+//     language_registry.add(javascript_language.clone());
+
+//     cx.add_model(|cx| {
+//         let (text, ranges) = marked_text_ranges(
+//             &"
+//                 <div>ˇ
+//                 </div>
+//                 <script>
+//                     init({ˇ
+//                     })
+//                 </script>
+//                 <span>ˇ
+//                 </span>
+//             "
+//             .unindent(),
+//             false,
+//         );
+
+//         let mut buffer = Buffer::new(0, cx.model_id() as u64, text);
+//         buffer.set_language_registry(language_registry);
+//         buffer.set_language(Some(html_language), cx);
+//         buffer.edit(
+//             ranges.into_iter().map(|range| (range, "\na")),
+//             Some(AutoindentMode::EachLine),
+//             cx,
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             "
+//                 <div>
+//                   a
+//                 </div>
+//                 <script>
+//                     init({
+//                             a
+//                     })
+//                 </script>
+//                 <span>
+//                   a
+//                 </span>
+//             "
+//             .unindent()
+//         );
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
+//     init_settings(cx, |settings| {
+//         settings.defaults.tab_size = Some(2.try_into().unwrap());
+//     });
+
+//     cx.add_model(|cx| {
+//         let mut buffer =
+//             Buffer::new(0, cx.model_id() as u64, "").with_language(Arc::new(ruby_lang()), cx);
+
+//         let text = r#"
+//             class C
+//             def a(b, c)
+//             puts b
+//             puts c
+//             rescue
+//             puts "errored"
+//             exit 1
+//             end
+//             end
+//         "#
+//         .unindent();
+
+//         buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
+
+//         assert_eq!(
+//             buffer.text(),
+//             r#"
+//                 class C
+//                   def a(b, c)
+//                     puts b
+//                     puts c
+//                   rescue
+//                     puts "errored"
+//                     exit 1
+//                   end
+//                 end
+//             "#
+//             .unindent()
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let language = Language::new(
+//             LanguageConfig {
+//                 name: "JavaScript".into(),
+//                 line_comment: Some("// ".into()),
+//                 brackets: BracketPairConfig {
+//                     pairs: vec![
+//                         BracketPair {
+//                             start: "{".into(),
+//                             end: "}".into(),
+//                             close: true,
+//                             newline: false,
+//                         },
+//                         BracketPair {
+//                             start: "'".into(),
+//                             end: "'".into(),
+//                             close: true,
+//                             newline: false,
+//                         },
+//                     ],
+//                     disabled_scopes_by_bracket_ix: vec![
+//                         Vec::new(), //
+//                         vec!["string".into()],
+//                     ],
+//                 },
+//                 overrides: [(
+//                     "element".into(),
+//                     LanguageConfigOverride {
+//                         line_comment: Override::Remove { remove: true },
+//                         block_comment: Override::Set(("{/*".into(), "*/}".into())),
+//                         ..Default::default()
+//                     },
+//                 )]
+//                 .into_iter()
+//                 .collect(),
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_typescript::language_tsx()),
+//         )
+//         .with_override_query(
+//             r#"
+//                 (jsx_element) @element
+//                 (string) @string
+//             "#,
+//         )
+//         .unwrap();
+
+//         let text = r#"a["b"] = <C d="e"></C>;"#;
+
+//         let buffer =
+//             Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(language), cx);
+//         let snapshot = buffer.snapshot();
+
+//         let config = snapshot.language_scope_at(0).unwrap();
+//         assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
+//         // Both bracket pairs are enabled
+//         assert_eq!(
+//             config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+//             &[true, true]
+//         );
+
+//         let string_config = snapshot.language_scope_at(3).unwrap();
+//         assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
+//         // Second bracket pair is disabled
+//         assert_eq!(
+//             string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+//             &[true, false]
+//         );
+
+//         let element_config = snapshot.language_scope_at(10).unwrap();
+//         assert_eq!(element_config.line_comment_prefix(), None);
+//         assert_eq!(
+//             element_config.block_comment_delimiters(),
+//             Some((&"{/*".into(), &"*/}".into()))
+//         );
+//         // Both bracket pairs are enabled
+//         assert_eq!(
+//             element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+//             &[true, true]
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_language_scope_at_with_rust(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 brackets: BracketPairConfig {
+//                     pairs: vec![
+//                         BracketPair {
+//                             start: "{".into(),
+//                             end: "}".into(),
+//                             close: true,
+//                             newline: false,
+//                         },
+//                         BracketPair {
+//                             start: "'".into(),
+//                             end: "'".into(),
+//                             close: true,
+//                             newline: false,
+//                         },
+//                     ],
+//                     disabled_scopes_by_bracket_ix: vec![
+//                         Vec::new(), //
+//                         vec!["string".into()],
+//                     ],
+//                 },
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_override_query(
+//             r#"
+//                 (string_literal) @string
+//             "#,
+//         )
+//         .unwrap();
+
+//         let text = r#"
+//             const S: &'static str = "hello";
+//         "#
+//         .unindent();
+
+//         let buffer = Buffer::new(0, cx.model_id() as u64, text.clone())
+//             .with_language(Arc::new(language), cx);
+//         let snapshot = buffer.snapshot();
+
+//         // By default, all brackets are enabled
+//         let config = snapshot.language_scope_at(0).unwrap();
+//         assert_eq!(
+//             config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+//             &[true, true]
+//         );
+
+//         // Within a string, the quotation brackets are disabled.
+//         let string_config = snapshot
+//             .language_scope_at(text.find("ello").unwrap())
+//             .unwrap();
+//         assert_eq!(
+//             string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+//             &[true, false]
+//         );
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
+//     init_settings(cx, |_| {});
+
+//     cx.add_model(|cx| {
+//         let text = r#"
+//             <ol>
+//             <% people.each do |person| %>
+//                 <li>
+//                     <%= person.name %>
+//                 </li>
+//             <% end %>
+//             </ol>
+//         "#
+//         .unindent();
+
+//         let language_registry = Arc::new(LanguageRegistry::test());
+//         language_registry.add(Arc::new(ruby_lang()));
+//         language_registry.add(Arc::new(html_lang()));
+//         language_registry.add(Arc::new(erb_lang()));
+
+//         let mut buffer = Buffer::new(0, cx.model_id() as u64, text);
+//         buffer.set_language_registry(language_registry.clone());
+//         buffer.set_language(
+//             language_registry
+//                 .language_for_name("ERB")
+//                 .now_or_never()
+//                 .unwrap()
+//                 .ok(),
+//             cx,
+//         );
+
+//         let snapshot = buffer.snapshot();
+//         let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
+//         assert_eq!(html_config.line_comment_prefix(), None);
+//         assert_eq!(
+//             html_config.block_comment_delimiters(),
+//             Some((&"<!--".into(), &"-->".into()))
+//         );
+
+//         let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
+//         assert_eq!(ruby_config.line_comment_prefix().unwrap().as_ref(), "# ");
+//         assert_eq!(ruby_config.block_comment_delimiters(), None);
+
+//         buffer
+//     });
+// }
+
+// #[gpui::test]
+// fn test_serialization(cx: &mut gpui::AppContext) {
+//     let mut now = Instant::now();
+
+//     let buffer1 = cx.add_model(|cx| {
+//         let mut buffer = Buffer::new(0, cx.model_id() as u64, "abc");
+//         buffer.edit([(3..3, "D")], None, cx);
+
+//         now += Duration::from_secs(1);
+//         buffer.start_transaction_at(now);
+//         buffer.edit([(4..4, "E")], None, cx);
+//         buffer.end_transaction_at(now, cx);
+//         assert_eq!(buffer.text(), "abcDE");
+
+//         buffer.undo(cx);
+//         assert_eq!(buffer.text(), "abcD");
+
+//         buffer.edit([(4..4, "F")], None, cx);
+//         assert_eq!(buffer.text(), "abcDF");
+//         buffer
+//     });
+//     assert_eq!(buffer1.read(cx).text(), "abcDF");
+
+//     let state = buffer1.read(cx).to_proto();
+//     let ops = cx
+//         .background()
+//         .block(buffer1.read(cx).serialize_ops(None, cx));
+//     let buffer2 = cx.add_model(|cx| {
+//         let mut buffer = Buffer::from_proto(1, state, None).unwrap();
+//         buffer
+//             .apply_ops(
+//                 ops.into_iter()
+//                     .map(|op| proto::deserialize_operation(op).unwrap()),
+//                 cx,
+//             )
+//             .unwrap();
+//         buffer
+//     });
+//     assert_eq!(buffer2.read(cx).text(), "abcDF");
+// }
+
+// #[gpui::test(iterations = 100)]
+// fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
+//     let min_peers = env::var("MIN_PEERS")
+//         .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
+//         .unwrap_or(1);
+//     let max_peers = env::var("MAX_PEERS")
+//         .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
+//         .unwrap_or(5);
+//     let operations = env::var("OPERATIONS")
+//         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+//         .unwrap_or(10);
+
+//     let base_text_len = rng.gen_range(0..10);
+//     let base_text = RandomCharIter::new(&mut rng)
+//         .take(base_text_len)
+//         .collect::<String>();
+//     let mut replica_ids = Vec::new();
+//     let mut buffers = Vec::new();
+//     let network = Rc::new(RefCell::new(Network::new(rng.clone())));
+//     let base_buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, base_text.as_str()));
+
+//     for i in 0..rng.gen_range(min_peers..=max_peers) {
+//         let buffer = cx.add_model(|cx| {
+//             let state = base_buffer.read(cx).to_proto();
+//             let ops = cx
+//                 .background()
+//                 .block(base_buffer.read(cx).serialize_ops(None, cx));
+//             let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
+//             buffer
+//                 .apply_ops(
+//                     ops.into_iter()
+//                         .map(|op| proto::deserialize_operation(op).unwrap()),
+//                     cx,
+//                 )
+//                 .unwrap();
+//             buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
+//             let network = network.clone();
+//             cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
+//                 if let Event::Operation(op) = event {
+//                     network
+//                         .borrow_mut()
+//                         .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
+//                 }
+//             })
+//             .detach();
+//             buffer
+//         });
+//         buffers.push(buffer);
+//         replica_ids.push(i as ReplicaId);
+//         network.borrow_mut().add_peer(i as ReplicaId);
+//         log::info!("Adding initial peer with replica id {}", i);
+//     }
+
+//     log::info!("initial text: {:?}", base_text);
+
+//     let mut now = Instant::now();
+//     let mut mutation_count = operations;
+//     let mut next_diagnostic_id = 0;
+//     let mut active_selections = BTreeMap::default();
+//     loop {
+//         let replica_index = rng.gen_range(0..replica_ids.len());
+//         let replica_id = replica_ids[replica_index];
+//         let buffer = &mut buffers[replica_index];
+//         let mut new_buffer = None;
+//         match rng.gen_range(0..100) {
+//             0..=29 if mutation_count != 0 => {
+//                 buffer.update(cx, |buffer, cx| {
+//                     buffer.start_transaction_at(now);
+//                     buffer.randomly_edit(&mut rng, 5, cx);
+//                     buffer.end_transaction_at(now, cx);
+//                     log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
+//                 });
+//                 mutation_count -= 1;
+//             }
+//             30..=39 if mutation_count != 0 => {
+//                 buffer.update(cx, |buffer, cx| {
+//                     if rng.gen_bool(0.2) {
+//                         log::info!("peer {} clearing active selections", replica_id);
+//                         active_selections.remove(&replica_id);
+//                         buffer.remove_active_selections(cx);
+//                     } else {
+//                         let mut selections = Vec::new();
+//                         for id in 0..rng.gen_range(1..=5) {
+//                             let range = buffer.random_byte_range(0, &mut rng);
+//                             selections.push(Selection {
+//                                 id,
+//                                 start: buffer.anchor_before(range.start),
+//                                 end: buffer.anchor_before(range.end),
+//                                 reversed: false,
+//                                 goal: SelectionGoal::None,
+//                             });
+//                         }
+//                         let selections: Arc<[Selection<Anchor>]> = selections.into();
+//                         log::info!(
+//                             "peer {} setting active selections: {:?}",
+//                             replica_id,
+//                             selections
+//                         );
+//                         active_selections.insert(replica_id, selections.clone());
+//                         buffer.set_active_selections(selections, false, Default::default(), cx);
+//                     }
+//                 });
+//                 mutation_count -= 1;
+//             }
+//             40..=49 if mutation_count != 0 && replica_id == 0 => {
+//                 let entry_count = rng.gen_range(1..=5);
+//                 buffer.update(cx, |buffer, cx| {
+//                     let diagnostics = DiagnosticSet::new(
+//                         (0..entry_count).map(|_| {
+//                             let range = buffer.random_byte_range(0, &mut rng);
+//                             let range = range.to_point_utf16(buffer);
+//                             let range = range.start..range.end;
+//                             DiagnosticEntry {
+//                                 range,
+//                                 diagnostic: Diagnostic {
+//                                     message: post_inc(&mut next_diagnostic_id).to_string(),
+//                                     ..Default::default()
+//                                 },
+//                             }
+//                         }),
+//                         buffer,
+//                     );
+//                     log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
+//                     buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
+//                 });
+//                 mutation_count -= 1;
+//             }
+//             50..=59 if replica_ids.len() < max_peers => {
+//                 let old_buffer_state = buffer.read(cx).to_proto();
+//                 let old_buffer_ops = cx
+//                     .background()
+//                     .block(buffer.read(cx).serialize_ops(None, cx));
+//                 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
+//                     .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
+//                     .choose(&mut rng)
+//                     .unwrap();
+//                 log::info!(
+//                     "Adding new replica {} (replicating from {})",
+//                     new_replica_id,
+//                     replica_id
+//                 );
+//                 new_buffer = Some(cx.add_model(|cx| {
+//                     let mut new_buffer =
+//                         Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
+//                     new_buffer
+//                         .apply_ops(
+//                             old_buffer_ops
+//                                 .into_iter()
+//                                 .map(|op| deserialize_operation(op).unwrap()),
+//                             cx,
+//                         )
+//                         .unwrap();
+//                     log::info!(
+//                         "New replica {} text: {:?}",
+//                         new_buffer.replica_id(),
+//                         new_buffer.text()
+//                     );
+//                     new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
+//                     let network = network.clone();
+//                     cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
+//                         if let Event::Operation(op) = event {
+//                             network.borrow_mut().broadcast(
+//                                 buffer.replica_id(),
+//                                 vec![proto::serialize_operation(op)],
+//                             );
+//                         }
+//                     })
+//                     .detach();
+//                     new_buffer
+//                 }));
+//                 network.borrow_mut().replicate(replica_id, new_replica_id);
+
+//                 if new_replica_id as usize == replica_ids.len() {
+//                     replica_ids.push(new_replica_id);
+//                 } else {
+//                     let new_buffer = new_buffer.take().unwrap();
+//                     while network.borrow().has_unreceived(new_replica_id) {
+//                         let ops = network
+//                             .borrow_mut()
+//                             .receive(new_replica_id)
+//                             .into_iter()
+//                             .map(|op| proto::deserialize_operation(op).unwrap());
+//                         if ops.len() > 0 {
+//                             log::info!(
+//                                 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
+//                                 new_replica_id,
+//                                 buffer.read(cx).version(),
+//                                 ops.len(),
+//                                 ops
+//                             );
+//                             new_buffer.update(cx, |new_buffer, cx| {
+//                                 new_buffer.apply_ops(ops, cx).unwrap();
+//                             });
+//                         }
+//                     }
+//                     buffers[new_replica_id as usize] = new_buffer;
+//                 }
+//             }
+//             60..=69 if mutation_count != 0 => {
+//                 buffer.update(cx, |buffer, cx| {
+//                     buffer.randomly_undo_redo(&mut rng, cx);
+//                     log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
+//                 });
+//                 mutation_count -= 1;
+//             }
+//             _ if network.borrow().has_unreceived(replica_id) => {
+//                 let ops = network
+//                     .borrow_mut()
+//                     .receive(replica_id)
+//                     .into_iter()
+//                     .map(|op| proto::deserialize_operation(op).unwrap());
+//                 if ops.len() > 0 {
+//                     log::info!(
+//                         "peer {} (version: {:?}) applying {} ops from the network. {:?}",
+//                         replica_id,
+//                         buffer.read(cx).version(),
+//                         ops.len(),
+//                         ops
+//                     );
+//                     buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
+//                 }
+//             }
+//             _ => {}
+//         }
+
+//         now += Duration::from_millis(rng.gen_range(0..=200));
+//         buffers.extend(new_buffer);
+
+//         for buffer in &buffers {
+//             buffer.read(cx).check_invariants();
+//         }
+
+//         if mutation_count == 0 && network.borrow().is_idle() {
+//             break;
+//         }
+//     }
+
+//     let first_buffer = buffers[0].read(cx).snapshot();
+//     for buffer in &buffers[1..] {
+//         let buffer = buffer.read(cx).snapshot();
+//         assert_eq!(
+//             buffer.version(),
+//             first_buffer.version(),
+//             "Replica {} version != Replica 0 version",
+//             buffer.replica_id()
+//         );
+//         assert_eq!(
+//             buffer.text(),
+//             first_buffer.text(),
+//             "Replica {} text != Replica 0 text",
+//             buffer.replica_id()
+//         );
+//         assert_eq!(
+//             buffer
+//                 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
+//                 .collect::<Vec<_>>(),
+//             first_buffer
+//                 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
+//                 .collect::<Vec<_>>(),
+//             "Replica {} diagnostics != Replica 0 diagnostics",
+//             buffer.replica_id()
+//         );
+//     }
+
+//     for buffer in &buffers {
+//         let buffer = buffer.read(cx).snapshot();
+//         let actual_remote_selections = buffer
+//             .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
+//             .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
+//             .collect::<Vec<_>>();
+//         let expected_remote_selections = active_selections
+//             .iter()
+//             .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
+//             .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
+//             .collect::<Vec<_>>();
+//         assert_eq!(
+//             actual_remote_selections,
+//             expected_remote_selections,
+//             "Replica {} remote selections != expected selections",
+//             buffer.replica_id()
+//         );
+//     }
+// }
+
+// #[test]
+// fn test_contiguous_ranges() {
+//     assert_eq!(
+//         contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
+//         &[1..4, 5..7, 9..13]
+//     );
+
+//     // Respects the `max_len` parameter
+//     assert_eq!(
+//         contiguous_ranges(
+//             [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
+//             3
+//         )
+//         .collect::<Vec<_>>(),
+//         &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
+//     );
+// }
+
+// #[gpui::test(iterations = 500)]
+// fn test_trailing_whitespace_ranges(mut rng: StdRng) {
+//     // Generate a random multi-line string containing
+//     // some lines with trailing whitespace.
+//     let mut text = String::new();
+//     for _ in 0..rng.gen_range(0..16) {
+//         for _ in 0..rng.gen_range(0..36) {
+//             text.push(match rng.gen_range(0..10) {
+//                 0..=1 => ' ',
+//                 3 => '\t',
+//                 _ => rng.gen_range('a'..'z'),
+//             });
+//         }
+//         text.push('\n');
+//     }
+
+//     match rng.gen_range(0..10) {
+//         // sometimes remove the last newline
+//         0..=1 => drop(text.pop()), //
+
+//         // sometimes add extra newlines
+//         2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
+//         _ => {}
+//     }
+
+//     let rope = Rope::from(text.as_str());
+//     let actual_ranges = trailing_whitespace_ranges(&rope);
+//     let expected_ranges = TRAILING_WHITESPACE_REGEX
+//         .find_iter(&text)
+//         .map(|m| m.range())
+//         .collect::<Vec<_>>();
+//     assert_eq!(
+//         actual_ranges,
+//         expected_ranges,
+//         "wrong ranges for text lines:\n{:?}",
+//         text.split("\n").collect::<Vec<_>>()
+//     );
+// }
+
+// fn ruby_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "Ruby".into(),
+//             path_suffixes: vec!["rb".to_string()],
+//             line_comment: Some("# ".into()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_ruby::language()),
+//     )
+//     .with_indents_query(
+//         r#"
+//             (class "end" @end) @indent
+//             (method "end" @end) @indent
+//             (rescue) @outdent
+//             (then) @indent
+//         "#,
+//     )
+//     .unwrap()
+// }
+
+// fn html_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "HTML".into(),
+//             block_comment: Some(("<!--".into(), "-->".into())),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_html::language()),
+//     )
+//     .with_indents_query(
+//         "
+//         (element
+//           (start_tag) @start
+//           (end_tag)? @end) @indent
+//         ",
+//     )
+//     .unwrap()
+//     .with_injection_query(
+//         r#"
+//         (script_element
+//             (raw_text) @content
+//             (#set! "language" "javascript"))
+//         "#,
+//     )
+//     .unwrap()
+// }
+
+// fn erb_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "ERB".into(),
+//             path_suffixes: vec!["erb".to_string()],
+//             block_comment: Some(("<%#".into(), "%>".into())),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_embedded_template::language()),
+//     )
+//     .with_injection_query(
+//         r#"
+//             (
+//                 (code) @content
+//                 (#set! "language" "ruby")
+//                 (#set! "combined")
+//             )
+
+//             (
+//                 (content) @content
+//                 (#set! "language" "html")
+//                 (#set! "combined")
+//             )
+//         "#,
+//     )
+//     .unwrap()
+// }
+
+// fn rust_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     )
+//     .with_indents_query(
+//         r#"
+//         (call_expression) @indent
+//         (field_expression) @indent
+//         (_ "(" ")" @end) @indent
+//         (_ "{" "}" @end) @indent
+//         "#,
+//     )
+//     .unwrap()
+//     .with_brackets_query(
+//         r#"
+//         ("{" @open "}" @close)
+//         "#,
+//     )
+//     .unwrap()
+//     .with_outline_query(
+//         r#"
+//         (struct_item
+//             "struct" @context
+//             name: (_) @name) @item
+//         (enum_item
+//             "enum" @context
+//             name: (_) @name) @item
+//         (enum_variant
+//             name: (_) @name) @item
+//         (field_declaration
+//             name: (_) @name) @item
+//         (impl_item
+//             "impl" @context
+//             trait: (_)? @name
+//             "for"? @context
+//             type: (_) @name) @item
+//         (function_item
+//             "fn" @context
+//             name: (_) @name) @item
+//         (mod_item
+//             "mod" @context
+//             name: (_) @name) @item
+//         "#,
+//     )
+//     .unwrap()
+// }
+
+// fn json_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "Json".into(),
+//             path_suffixes: vec!["js".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_json::language()),
+//     )
+// }
+
+// fn javascript_lang() -> Language {
+//     Language::new(
+//         LanguageConfig {
+//             name: "JavaScript".into(),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_typescript::language_tsx()),
+//     )
+//     .with_brackets_query(
+//         r#"
+//         ("{" @open "}" @close)
+//         ("(" @open ")" @close)
+//         "#,
+//     )
+//     .unwrap()
+//     .with_indents_query(
+//         r#"
+//         (object "}" @end) @indent
+//         "#,
+//     )
+//     .unwrap()
+// }
+
+// fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
+//     buffer.read_with(cx, |buffer, _| {
+//         let snapshot = buffer.snapshot();
+//         let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
+//         layers[0].node().to_sexp()
+//     })
+// }
+
+// // Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
+// fn assert_bracket_pairs(
+//     selection_text: &'static str,
+//     bracket_pair_texts: Vec<&'static str>,
+//     language: Language,
+//     cx: &mut AppContext,
+// ) {
+//     let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
+//     let buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, expected_text.clone())
+//             .with_language(Arc::new(language), cx)
+//     });
+//     let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
+
+//     let selection_range = selection_ranges[0].clone();
+
+//     let bracket_pairs = bracket_pair_texts
+//         .into_iter()
+//         .map(|pair_text| {
+//             let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
+//             assert_eq!(bracket_text, expected_text);
+//             (ranges[0].clone(), ranges[1].clone())
+//         })
+//         .collect::<Vec<_>>();
+
+//     assert_set_eq!(
+//         buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
+//         bracket_pairs
+//     );
+// }
+
+// fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
+//     cx.set_global(SettingsStore::test(cx));
+//     crate::init(cx);
+//     cx.update_global::<SettingsStore, _, _>(|settings, cx| {
+//         settings.update_user_settings::<AllLanguageSettings>(cx, f);
+//     });
+// }

crates/language2/src/diagnostic_set.rs 🔗

@@ -1,6 +1,6 @@
 use crate::Diagnostic;
 use collections::HashMap;
-use lsp::LanguageServerId;
+use lsp2::LanguageServerId;
 use std::{
     cmp::{Ordering, Reverse},
     iter,
@@ -37,14 +37,14 @@ pub struct Summary {
 
 impl<T> DiagnosticEntry<T> {
     // Used to provide diagnostic context to lsp codeAction request
-    pub fn to_lsp_diagnostic_stub(&self) -> lsp::Diagnostic {
+    pub fn to_lsp_diagnostic_stub(&self) -> lsp2::Diagnostic {
         let code = self
             .diagnostic
             .code
             .clone()
-            .map(lsp::NumberOrString::String);
+            .map(lsp2::NumberOrString::String);
 
-        lsp::Diagnostic {
+        lsp2::Diagnostic {
             code,
             severity: Some(self.diagnostic.severity),
             ..Default::default()

crates/language2/src/highlight_map.rs 🔗

@@ -1,6 +1,6 @@
 use gpui2::HighlightStyle;
 use std::sync::Arc;
-use theme::SyntaxTheme;
+use theme2::SyntaxTheme;
 
 #[derive(Clone, Debug)]
 pub struct HighlightMap(Arc<[HighlightId]>);
@@ -76,36 +76,36 @@ impl Default for HighlightId {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use gpui::color::Color;
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use gpui2::color::Color;
 
-    #[test]
-    fn test_highlight_map() {
-        let theme = SyntaxTheme::new(
-            [
-                ("function", Color::from_u32(0x100000ff)),
-                ("function.method", Color::from_u32(0x200000ff)),
-                ("function.async", Color::from_u32(0x300000ff)),
-                ("variable.builtin.self.rust", Color::from_u32(0x400000ff)),
-                ("variable.builtin", Color::from_u32(0x500000ff)),
-                ("variable", Color::from_u32(0x600000ff)),
-            ]
-            .iter()
-            .map(|(name, color)| (name.to_string(), (*color).into()))
-            .collect(),
-        );
+//     #[test]
+//     fn test_highlight_map() {
+//         let theme = SyntaxTheme::new(
+//             [
+//                 ("function", Color::from_u32(0x100000ff)),
+//                 ("function.method", Color::from_u32(0x200000ff)),
+//                 ("function.async", Color::from_u32(0x300000ff)),
+//                 ("variable.builtin.self.rust", Color::from_u32(0x400000ff)),
+//                 ("variable.builtin", Color::from_u32(0x500000ff)),
+//                 ("variable", Color::from_u32(0x600000ff)),
+//             ]
+//             .iter()
+//             .map(|(name, color)| (name.to_string(), (*color).into()))
+//             .collect(),
+//         );
 
-        let capture_names = &[
-            "function.special".to_string(),
-            "function.async.rust".to_string(),
-            "variable.builtin.self".to_string(),
-        ];
+//         let capture_names = &[
+//             "function.special".to_string(),
+//             "function.async.rust".to_string(),
+//             "variable.builtin.self".to_string(),
+//         ];
 
-        let map = HighlightMap::new(capture_names, &theme);
-        assert_eq!(map.get(0).name(&theme), Some("function"));
-        assert_eq!(map.get(1).name(&theme), Some("function.async"));
-        assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
-    }
-}
+//         let map = HighlightMap::new(capture_names, &theme);
+//         assert_eq!(map.get(0).name(&theme), Some("function"));
+//         assert_eq!(map.get(1).name(&theme), Some("function.async"));
+//         assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
+//     }
+// }

crates/language2/src/language2.rs 🔗

@@ -20,7 +20,7 @@ use futures::{
 use gpui2::{AppContext, AsyncAppContext, Executor, Task};
 pub use highlight_map::HighlightMap;
 use lazy_static::lazy_static;
-use lsp::{CodeActionKind, LanguageServerBinary};
+use lsp2::{CodeActionKind, LanguageServerBinary};
 use parking_lot::{Mutex, RwLock};
 use postage::watch;
 use regex::Regex;
@@ -42,7 +42,7 @@ use std::{
     },
 };
 use syntax_map::SyntaxSnapshot;
-use theme::{SyntaxTheme, Theme};
+use theme2::{SyntaxTheme, Theme};
 use tree_sitter::{self, Query};
 use unicase::UniCase;
 use util::{http::HttpClient, paths::PathExt};
@@ -51,7 +51,7 @@ use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
 pub use buffer::Operation;
 pub use buffer::*;
 pub use diagnostic_set::DiagnosticEntry;
-pub use lsp::LanguageServerId;
+pub use lsp2::LanguageServerId;
 pub use outline::{Outline, OutlineItem};
 pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo};
 pub use text::LineEnding;
@@ -98,7 +98,7 @@ lazy_static! {
 }
 
 pub trait ToLspPosition {
-    fn to_lsp_position(self) -> lsp::Position;
+    fn to_lsp_position(self) -> lsp2::Position;
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -201,17 +201,17 @@ impl CachedLspAdapter {
         self.adapter.workspace_configuration(cx)
     }
 
-    pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
+    pub fn process_diagnostics(&self, params: &mut lsp2::PublishDiagnosticsParams) {
         self.adapter.process_diagnostics(params)
     }
 
-    pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) {
+    pub async fn process_completion(&self, completion_item: &mut lsp2::CompletionItem) {
         self.adapter.process_completion(completion_item).await
     }
 
     pub async fn label_for_completion(
         &self,
-        completion_item: &lsp::CompletionItem,
+        completion_item: &lsp2::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         self.adapter
@@ -222,7 +222,7 @@ impl CachedLspAdapter {
     pub async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp::SymbolKind,
+        kind: lsp2::SymbolKind,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         self.adapter.label_for_symbol(name, kind, language).await
@@ -287,13 +287,13 @@ pub trait LspAdapter: 'static + Send + Sync {
         container_dir: PathBuf,
     ) -> Option<LanguageServerBinary>;
 
-    fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
+    fn process_diagnostics(&self, _: &mut lsp2::PublishDiagnosticsParams) {}
 
-    async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
+    async fn process_completion(&self, _: &mut lsp2::CompletionItem) {}
 
     async fn label_for_completion(
         &self,
-        _: &lsp::CompletionItem,
+        _: &lsp2::CompletionItem,
         _: &Arc<Language>,
     ) -> Option<CodeLabel> {
         None
@@ -302,7 +302,7 @@ pub trait LspAdapter: 'static + Send + Sync {
     async fn label_for_symbol(
         &self,
         _: &str,
-        _: lsp::SymbolKind,
+        _: lsp2::SymbolKind,
         _: &Arc<Language>,
     ) -> Option<CodeLabel> {
         None
@@ -494,8 +494,8 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
 pub struct FakeLspAdapter {
     pub name: &'static str,
     pub initialization_options: Option<Value>,
-    pub capabilities: lsp::ServerCapabilities,
-    pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
+    pub capabilities: lsp2::ServerCapabilities,
+    pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp2::FakeLanguageServer)>>,
     pub disk_based_diagnostics_progress_token: Option<String>,
     pub disk_based_diagnostics_sources: Vec<String>,
     pub enabled_formatters: Vec<BundledFormatter>,
@@ -550,7 +550,7 @@ pub struct Language {
 
     #[cfg(any(test, feature = "test-support"))]
     fake_adapter: Option<(
-        mpsc::UnboundedSender<lsp::FakeLanguageServer>,
+        mpsc::UnboundedSender<lsp2::FakeLanguageServer>,
         Arc<FakeLspAdapter>,
     )>,
 }
@@ -667,7 +667,7 @@ struct LanguageRegistryState {
 
 pub struct PendingLanguageServer {
     pub server_id: LanguageServerId,
-    pub task: Task<Result<Option<lsp::LanguageServer>>>,
+    pub task: Task<Result<Option<lsp2::LanguageServer>>>,
     pub container_dir: Option<Arc<Path>>,
 }
 
@@ -922,7 +922,7 @@ impl LanguageRegistry {
         if language.fake_adapter.is_some() {
             let task = cx.spawn(|cx| async move {
                 let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap();
-                let (server, mut fake_server) = lsp::LanguageServer::fake(
+                let (server, mut fake_server) = lsp2::LanguageServer::fake(
                     fake_adapter.name.to_string(),
                     fake_adapter.capabilities.clone(),
                     cx.clone(),
@@ -933,10 +933,10 @@ impl LanguageRegistry {
                 }
 
                 let servers_tx = servers_tx.clone();
-                cx.background()
+                cx.executor()
                     .spawn(async move {
                         if fake_server
-                            .try_receive_notification::<lsp::notification::Initialized>()
+                            .try_receive_notification::<lsp2::notification::Initialized>()
                             .await
                             .is_some()
                         {
@@ -970,18 +970,22 @@ impl LanguageRegistry {
 
         let task = {
             let container_dir = container_dir.clone();
-            cx.spawn(|mut cx| async move {
+            cx.spawn(move |mut cx| async move {
                 login_shell_env_loaded.await;
 
-                let mut lock = this.lsp_binary_paths.lock();
-                let entry = lock
+                let entry = this
+                    .lsp_binary_paths
+                    .lock()
                     .entry(adapter.name.clone())
                     .or_insert_with(|| {
+                        let adapter = adapter.clone();
+                        let language = language.clone();
+                        let delegate = delegate.clone();
                         cx.spawn(|cx| {
                             get_binary(
-                                adapter.clone(),
-                                language.clone(),
-                                delegate.clone(),
+                                adapter,
+                                language,
+                                delegate,
                                 container_dir,
                                 lsp_binary_statuses,
                                 cx,
@@ -991,9 +995,8 @@ impl LanguageRegistry {
                         .shared()
                     })
                     .clone();
-                drop(lock);
 
-                let binary = match entry.clone().await.log_err() {
+                let binary = match entry.await.log_err() {
                     Some(binary) => binary,
                     None => return Ok(None),
                 };
@@ -1004,7 +1007,7 @@ impl LanguageRegistry {
                     }
                 }
 
-                Ok(Some(lsp::LanguageServer::new(
+                Ok(Some(lsp2::LanguageServer::new(
                     server_id,
                     binary,
                     &root_path,
@@ -1486,7 +1489,7 @@ impl Language {
     pub async fn set_fake_lsp_adapter(
         &mut self,
         fake_lsp_adapter: Arc<FakeLspAdapter>,
-    ) -> mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
+    ) -> mpsc::UnboundedReceiver<lsp2::FakeLanguageServer> {
         let (servers_tx, servers_rx) = mpsc::unbounded();
         self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone()));
         let adapter = CachedLspAdapter::new(Arc::new(fake_lsp_adapter)).await;
@@ -1516,7 +1519,7 @@ impl Language {
         None
     }
 
-    pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp::CompletionItem) {
+    pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp2::CompletionItem) {
         for adapter in &self.adapters {
             adapter.process_completion(completion).await;
         }
@@ -1524,7 +1527,7 @@ impl Language {
 
     pub async fn label_for_completion(
         self: &Arc<Self>,
-        completion: &lsp::CompletionItem,
+        completion: &lsp2::CompletionItem,
     ) -> Option<CodeLabel> {
         self.adapters
             .first()
@@ -1536,7 +1539,7 @@ impl Language {
     pub async fn label_for_symbol(
         self: &Arc<Self>,
         name: &str,
-        kind: lsp::SymbolKind,
+        kind: lsp2::SymbolKind,
     ) -> Option<CodeLabel> {
         self.adapters
             .first()
@@ -1756,7 +1759,7 @@ impl Default for FakeLspAdapter {
     fn default() -> Self {
         Self {
             name: "the-fake-language-server",
-            capabilities: lsp::LanguageServer::full_capabilities(),
+            capabilities: lsp2::LanguageServer::full_capabilities(),
             initializer: None,
             disk_based_diagnostics_progress_token: None,
             initialization_options: None,
@@ -1805,7 +1808,7 @@ impl LspAdapter for Arc<FakeLspAdapter> {
         unreachable!();
     }
 
-    fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
+    fn process_diagnostics(&self, _: &mut lsp2::PublishDiagnosticsParams) {}
 
     async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
         self.disk_based_diagnostics_sources.clone()
@@ -1835,22 +1838,22 @@ fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)])
     }
 }
 
-pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
-    lsp::Position::new(point.row, point.column)
+pub fn point_to_lsp(point: PointUtf16) -> lsp2::Position {
+    lsp2::Position::new(point.row, point.column)
 }
 
-pub fn point_from_lsp(point: lsp::Position) -> Unclipped<PointUtf16> {
+pub fn point_from_lsp(point: lsp2::Position) -> Unclipped<PointUtf16> {
     Unclipped(PointUtf16::new(point.line, point.character))
 }
 
-pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp::Range {
-    lsp::Range {
+pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp2::Range {
+    lsp2::Range {
         start: point_to_lsp(range.start),
         end: point_to_lsp(range.end),
     }
 }
 
-pub fn range_from_lsp(range: lsp::Range) -> Range<Unclipped<PointUtf16>> {
+pub fn range_from_lsp(range: lsp2::Range) -> Range<Unclipped<PointUtf16>> {
     let mut start = point_from_lsp(range.start);
     let mut end = point_from_lsp(range.end);
     if start > end {

crates/language2/src/outline.rs 🔗

@@ -1,6 +1,6 @@
-use fuzzy::{StringMatch, StringMatchCandidate};
+use fuzzy2::{StringMatch, StringMatchCandidate};
 use gpui2::{Executor, HighlightStyle};
-use std::{ops::Range, sync::Arc};
+use std::ops::Range;
 
 #[derive(Debug)]
 pub struct Outline<T> {
@@ -61,7 +61,7 @@ impl<T> Outline<T> {
         let query = query.trim_start();
         let is_path_query = query.contains(' ');
         let smart_case = query.chars().any(|c| c.is_uppercase());
-        let mut matches = fuzzy::match_strings(
+        let mut matches = fuzzy2::match_strings(
             if is_path_query {
                 &self.path_candidates
             } else {

crates/language2/src/proto.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 };
 use anyhow::{anyhow, Result};
 use clock::ReplicaId;
-use lsp::{DiagnosticSeverity, LanguageServerId};
+use lsp2::{DiagnosticSeverity, LanguageServerId};
 use rpc::proto;
 use std::{ops::Range, sync::Arc};
 use text::*;

crates/lsp2/Cargo.toml 🔗

@@ -0,0 +1,38 @@
+[package]
+name = "lsp2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/lsp2.rs"
+doctest = false
+
+[features]
+test-support = ["async-pipe"]
+
+[dependencies]
+collections = { path = "../collections" }
+gpui2 = { path = "../gpui2" }
+util = { path = "../util" }
+
+anyhow.workspace = true
+async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true }
+futures.workspace = true
+log.workspace = true
+lsp-types = { git = "https://github.com/zed-industries/lsp-types", branch = "updated-completion-list-item-defaults" }
+parking_lot.workspace = true
+postage.workspace = true
+serde.workspace = true
+serde_derive.workspace = true
+serde_json.workspace = true
+smol.workspace = true
+
+[dev-dependencies]
+gpui2 = { path = "../gpui2", features = ["test-support"] }
+util = { path = "../util", features = ["test-support"] }
+
+async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
+ctor.workspace = true
+env_logger.workspace = true
+unindent.workspace = true

crates/lsp2/src/lsp2.rs 🔗

@@ -0,0 +1,1167 @@
+use log::warn;
+pub use lsp_types::request::*;
+pub use lsp_types::*;
+
+use anyhow::{anyhow, Context, Result};
+use collections::HashMap;
+use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite, FutureExt};
+use gpui2::{AsyncAppContext, Executor, Task};
+use parking_lot::Mutex;
+use postage::{barrier, prelude::Stream};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use serde_json::{json, value::RawValue, Value};
+use smol::{
+    channel,
+    io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
+    process::{self, Child},
+};
+use std::{
+    ffi::OsString,
+    fmt,
+    future::Future,
+    io::Write,
+    path::PathBuf,
+    str::{self, FromStr as _},
+    sync::{
+        atomic::{AtomicUsize, Ordering::SeqCst},
+        Arc, Weak,
+    },
+    time::{Duration, Instant},
+};
+use std::{path::Path, process::Stdio};
+use util::{ResultExt, TryFutureExt};
+
+const JSON_RPC_VERSION: &str = "2.0";
+const CONTENT_LEN_HEADER: &str = "Content-Length: ";
+const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2);
+
+type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
+type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>)>;
+type IoHandler = Box<dyn Send + FnMut(IoKind, &str)>;
+
+#[derive(Debug, Clone, Copy)]
+pub enum IoKind {
+    StdOut,
+    StdIn,
+    StdErr,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct LanguageServerBinary {
+    pub path: PathBuf,
+    pub arguments: Vec<OsString>,
+}
+
+pub struct LanguageServer {
+    server_id: LanguageServerId,
+    next_id: AtomicUsize,
+    outbound_tx: channel::Sender<String>,
+    name: String,
+    capabilities: ServerCapabilities,
+    code_action_kinds: Option<Vec<CodeActionKind>>,
+    notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
+    response_handlers: Arc<Mutex<Option<HashMap<usize, ResponseHandler>>>>,
+    io_handlers: Arc<Mutex<HashMap<usize, IoHandler>>>,
+    executor: Executor,
+    #[allow(clippy::type_complexity)]
+    io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
+    output_done_rx: Mutex<Option<barrier::Receiver>>,
+    root_path: PathBuf,
+    _server: Option<Mutex<Child>>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+pub struct LanguageServerId(pub usize);
+
+pub enum Subscription {
+    Notification {
+        method: &'static str,
+        notification_handlers: Option<Arc<Mutex<HashMap<&'static str, NotificationHandler>>>>,
+    },
+    Io {
+        id: usize,
+        io_handlers: Option<Weak<Mutex<HashMap<usize, IoHandler>>>>,
+    },
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Request<'a, T> {
+    jsonrpc: &'static str,
+    id: usize,
+    method: &'a str,
+    params: T,
+}
+
+#[derive(Serialize, Deserialize)]
+struct AnyResponse<'a> {
+    jsonrpc: &'a str,
+    id: usize,
+    #[serde(default)]
+    error: Option<Error>,
+    #[serde(borrow)]
+    result: Option<&'a RawValue>,
+}
+
+#[derive(Serialize)]
+struct Response<T> {
+    jsonrpc: &'static str,
+    id: usize,
+    result: Option<T>,
+    error: Option<Error>,
+}
+
+#[derive(Serialize, Deserialize)]
+struct Notification<'a, T> {
+    jsonrpc: &'static str,
+    #[serde(borrow)]
+    method: &'a str,
+    params: T,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+struct AnyNotification<'a> {
+    #[serde(default)]
+    id: Option<usize>,
+    #[serde(borrow)]
+    method: &'a str,
+    #[serde(borrow, default)]
+    params: Option<&'a RawValue>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Error {
+    message: String,
+}
+
+impl LanguageServer {
+    pub fn new(
+        server_id: LanguageServerId,
+        binary: LanguageServerBinary,
+        root_path: &Path,
+        code_action_kinds: Option<Vec<CodeActionKind>>,
+        cx: AsyncAppContext,
+    ) -> Result<Self> {
+        let working_dir = if root_path.is_dir() {
+            root_path
+        } else {
+            root_path.parent().unwrap_or_else(|| Path::new("/"))
+        };
+
+        let mut server = process::Command::new(&binary.path)
+            .current_dir(working_dir)
+            .args(binary.arguments)
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .kill_on_drop(true)
+            .spawn()?;
+
+        let stdin = server.stdin.take().unwrap();
+        let stdout = server.stdout.take().unwrap();
+        let stderr = server.stderr.take().unwrap();
+        let mut server = Self::new_internal(
+            server_id.clone(),
+            stdin,
+            stdout,
+            Some(stderr),
+            Some(server),
+            root_path,
+            code_action_kinds,
+            cx,
+            move |notification| {
+                log::info!(
+                    "{} unhandled notification {}:\n{}",
+                    server_id,
+                    notification.method,
+                    serde_json::to_string_pretty(
+                        &notification
+                            .params
+                            .and_then(|params| Value::from_str(params.get()).ok())
+                            .unwrap_or(Value::Null)
+                    )
+                    .unwrap(),
+                );
+            },
+        );
+
+        if let Some(name) = binary.path.file_name() {
+            server.name = name.to_string_lossy().to_string();
+        }
+
+        Ok(server)
+    }
+
+    fn new_internal<Stdin, Stdout, Stderr, F>(
+        server_id: LanguageServerId,
+        stdin: Stdin,
+        stdout: Stdout,
+        stderr: Option<Stderr>,
+        server: Option<Child>,
+        root_path: &Path,
+        code_action_kinds: Option<Vec<CodeActionKind>>,
+        cx: AsyncAppContext,
+        on_unhandled_notification: F,
+    ) -> Self
+    where
+        Stdin: AsyncWrite + Unpin + Send + 'static,
+        Stdout: AsyncRead + Unpin + Send + 'static,
+        Stderr: AsyncRead + Unpin + Send + 'static,
+        F: FnMut(AnyNotification) + 'static + Send + Sync + Clone,
+    {
+        let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
+        let (output_done_tx, output_done_rx) = barrier::channel();
+        let notification_handlers =
+            Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default()));
+        let response_handlers =
+            Arc::new(Mutex::new(Some(HashMap::<_, ResponseHandler>::default())));
+        let io_handlers = Arc::new(Mutex::new(HashMap::default()));
+
+        let stdout_input_task = cx.spawn({
+            let on_unhandled_notification = on_unhandled_notification.clone();
+            let notification_handlers = notification_handlers.clone();
+            let response_handlers = response_handlers.clone();
+            let io_handlers = io_handlers.clone();
+            move |cx| {
+                Self::handle_input(
+                    stdout,
+                    on_unhandled_notification,
+                    notification_handlers,
+                    response_handlers,
+                    io_handlers,
+                    cx,
+                )
+                .log_err()
+            }
+        });
+        let stderr_input_task = stderr
+            .map(|stderr| {
+                let io_handlers = io_handlers.clone();
+                cx.spawn(|_| Self::handle_stderr(stderr, io_handlers).log_err())
+            })
+            .unwrap_or_else(|| Task::Ready(Some(None)));
+        let input_task = cx.spawn(|_| async move {
+            let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
+            stdout.or(stderr)
+        });
+        let output_task = cx.executor().spawn({
+            Self::handle_output(
+                stdin,
+                outbound_rx,
+                output_done_tx,
+                response_handlers.clone(),
+                io_handlers.clone(),
+            )
+            .log_err()
+        });
+
+        Self {
+            server_id,
+            notification_handlers,
+            response_handlers,
+            io_handlers,
+            name: Default::default(),
+            capabilities: Default::default(),
+            code_action_kinds,
+            next_id: Default::default(),
+            outbound_tx,
+            executor: cx.executor().clone(),
+            io_tasks: Mutex::new(Some((input_task, output_task))),
+            output_done_rx: Mutex::new(Some(output_done_rx)),
+            root_path: root_path.to_path_buf(),
+            _server: server.map(|server| Mutex::new(server)),
+        }
+    }
+
+    pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
+        self.code_action_kinds.clone()
+    }
+
+    async fn handle_input<Stdout, F>(
+        stdout: Stdout,
+        mut on_unhandled_notification: F,
+        notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
+        response_handlers: Arc<Mutex<Option<HashMap<usize, ResponseHandler>>>>,
+        io_handlers: Arc<Mutex<HashMap<usize, IoHandler>>>,
+        cx: AsyncAppContext,
+    ) -> anyhow::Result<()>
+    where
+        Stdout: AsyncRead + Unpin + Send + 'static,
+        F: FnMut(AnyNotification) + 'static + Send,
+    {
+        let mut stdout = BufReader::new(stdout);
+        let _clear_response_handlers = util::defer({
+            let response_handlers = response_handlers.clone();
+            move || {
+                response_handlers.lock().take();
+            }
+        });
+        let mut buffer = Vec::new();
+        loop {
+            buffer.clear();
+            stdout.read_until(b'\n', &mut buffer).await?;
+            stdout.read_until(b'\n', &mut buffer).await?;
+            let header = std::str::from_utf8(&buffer)?;
+            let message_len: usize = header
+                .strip_prefix(CONTENT_LEN_HEADER)
+                .ok_or_else(|| anyhow!("invalid LSP message header {header:?}"))?
+                .trim_end()
+                .parse()?;
+
+            buffer.resize(message_len, 0);
+            stdout.read_exact(&mut buffer).await?;
+
+            if let Ok(message) = str::from_utf8(&buffer) {
+                log::trace!("incoming message: {}", message);
+                for handler in io_handlers.lock().values_mut() {
+                    handler(IoKind::StdOut, message);
+                }
+            }
+
+            if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
+                if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
+                    handler(
+                        msg.id,
+                        &msg.params.map(|params| params.get()).unwrap_or("null"),
+                        cx.clone(),
+                    );
+                } else {
+                    on_unhandled_notification(msg);
+                }
+            } else if let Ok(AnyResponse {
+                id, error, result, ..
+            }) = serde_json::from_slice(&buffer)
+            {
+                if let Some(handler) = response_handlers
+                    .lock()
+                    .as_mut()
+                    .and_then(|handlers| handlers.remove(&id))
+                {
+                    if let Some(error) = error {
+                        handler(Err(error));
+                    } else if let Some(result) = result {
+                        handler(Ok(result.get().into()));
+                    } else {
+                        handler(Ok("null".into()));
+                    }
+                }
+            } else {
+                warn!(
+                    "failed to deserialize LSP message:\n{}",
+                    std::str::from_utf8(&buffer)?
+                );
+            }
+
+            // Don't starve the main thread when receiving lots of messages at once.
+            smol::future::yield_now().await;
+        }
+    }
+
+    async fn handle_stderr<Stderr>(
+        stderr: Stderr,
+        io_handlers: Arc<Mutex<HashMap<usize, IoHandler>>>,
+    ) -> anyhow::Result<()>
+    where
+        Stderr: AsyncRead + Unpin + Send + 'static,
+    {
+        let mut stderr = BufReader::new(stderr);
+        let mut buffer = Vec::new();
+        loop {
+            buffer.clear();
+            stderr.read_until(b'\n', &mut buffer).await?;
+            if let Ok(message) = str::from_utf8(&buffer) {
+                log::trace!("incoming stderr message:{message}");
+                for handler in io_handlers.lock().values_mut() {
+                    handler(IoKind::StdErr, message);
+                }
+            }
+
+            // Don't starve the main thread when receiving lots of messages at once.
+            smol::future::yield_now().await;
+        }
+    }
+
+    async fn handle_output<Stdin>(
+        stdin: Stdin,
+        outbound_rx: channel::Receiver<String>,
+        output_done_tx: barrier::Sender,
+        response_handlers: Arc<Mutex<Option<HashMap<usize, ResponseHandler>>>>,
+        io_handlers: Arc<Mutex<HashMap<usize, IoHandler>>>,
+    ) -> anyhow::Result<()>
+    where
+        Stdin: AsyncWrite + Unpin + Send + 'static,
+    {
+        let mut stdin = BufWriter::new(stdin);
+        let _clear_response_handlers = util::defer({
+            let response_handlers = response_handlers.clone();
+            move || {
+                response_handlers.lock().take();
+            }
+        });
+        let mut content_len_buffer = Vec::new();
+        while let Ok(message) = outbound_rx.recv().await {
+            log::trace!("outgoing message:{}", message);
+            for handler in io_handlers.lock().values_mut() {
+                handler(IoKind::StdIn, &message);
+            }
+
+            content_len_buffer.clear();
+            write!(content_len_buffer, "{}", message.len()).unwrap();
+            stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?;
+            stdin.write_all(&content_len_buffer).await?;
+            stdin.write_all("\r\n\r\n".as_bytes()).await?;
+            stdin.write_all(message.as_bytes()).await?;
+            stdin.flush().await?;
+        }
+        drop(output_done_tx);
+        Ok(())
+    }
+
+    /// Initializes a language server.
+    /// Note that `options` is used directly to construct [`InitializeParams`],
+    /// which is why it is owned.
+    pub async fn initialize(mut self, options: Option<Value>) -> Result<Arc<Self>> {
+        let root_uri = Url::from_file_path(&self.root_path).unwrap();
+        #[allow(deprecated)]
+        let params = InitializeParams {
+            process_id: Default::default(),
+            root_path: Default::default(),
+            root_uri: Some(root_uri.clone()),
+            initialization_options: options,
+            capabilities: ClientCapabilities {
+                workspace: Some(WorkspaceClientCapabilities {
+                    configuration: Some(true),
+                    did_change_watched_files: Some(DidChangeWatchedFilesClientCapabilities {
+                        dynamic_registration: Some(true),
+                        relative_pattern_support: Some(true),
+                    }),
+                    did_change_configuration: Some(DynamicRegistrationClientCapabilities {
+                        dynamic_registration: Some(true),
+                    }),
+                    workspace_folders: Some(true),
+                    symbol: Some(WorkspaceSymbolClientCapabilities {
+                        resolve_support: None,
+                        ..WorkspaceSymbolClientCapabilities::default()
+                    }),
+                    inlay_hint: Some(InlayHintWorkspaceClientCapabilities {
+                        refresh_support: Some(true),
+                    }),
+                    ..Default::default()
+                }),
+                text_document: Some(TextDocumentClientCapabilities {
+                    definition: Some(GotoCapability {
+                        link_support: Some(true),
+                        ..Default::default()
+                    }),
+                    code_action: Some(CodeActionClientCapabilities {
+                        code_action_literal_support: Some(CodeActionLiteralSupport {
+                            code_action_kind: CodeActionKindLiteralSupport {
+                                value_set: vec![
+                                    CodeActionKind::REFACTOR.as_str().into(),
+                                    CodeActionKind::QUICKFIX.as_str().into(),
+                                    CodeActionKind::SOURCE.as_str().into(),
+                                ],
+                            },
+                        }),
+                        data_support: Some(true),
+                        resolve_support: Some(CodeActionCapabilityResolveSupport {
+                            properties: vec!["edit".to_string(), "command".to_string()],
+                        }),
+                        ..Default::default()
+                    }),
+                    completion: Some(CompletionClientCapabilities {
+                        completion_item: Some(CompletionItemCapability {
+                            snippet_support: Some(true),
+                            resolve_support: Some(CompletionItemCapabilityResolveSupport {
+                                properties: vec!["additionalTextEdits".to_string()],
+                            }),
+                            ..Default::default()
+                        }),
+                        completion_list: Some(CompletionListCapability {
+                            item_defaults: Some(vec![
+                                "commitCharacters".to_owned(),
+                                "editRange".to_owned(),
+                                "insertTextMode".to_owned(),
+                                "data".to_owned(),
+                            ]),
+                        }),
+                        ..Default::default()
+                    }),
+                    rename: Some(RenameClientCapabilities {
+                        prepare_support: Some(true),
+                        ..Default::default()
+                    }),
+                    hover: Some(HoverClientCapabilities {
+                        content_format: Some(vec![MarkupKind::Markdown]),
+                        ..Default::default()
+                    }),
+                    inlay_hint: Some(InlayHintClientCapabilities {
+                        resolve_support: Some(InlayHintResolveClientCapabilities {
+                            properties: vec![
+                                "textEdits".to_string(),
+                                "tooltip".to_string(),
+                                "label.tooltip".to_string(),
+                                "label.location".to_string(),
+                                "label.command".to_string(),
+                            ],
+                        }),
+                        dynamic_registration: Some(false),
+                    }),
+                    ..Default::default()
+                }),
+                experimental: Some(json!({
+                    "serverStatusNotification": true,
+                })),
+                window: Some(WindowClientCapabilities {
+                    work_done_progress: Some(true),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
+            trace: Default::default(),
+            workspace_folders: Some(vec![WorkspaceFolder {
+                uri: root_uri,
+                name: Default::default(),
+            }]),
+            client_info: Default::default(),
+            locale: Default::default(),
+        };
+
+        let response = self.request::<request::Initialize>(params).await?;
+        if let Some(info) = response.server_info {
+            self.name = info.name;
+        }
+        self.capabilities = response.capabilities;
+
+        self.notify::<notification::Initialized>(InitializedParams {})?;
+        Ok(Arc::new(self))
+    }
+
+    pub fn shutdown(&self) -> Option<impl 'static + Send + Future<Output = Option<()>>> {
+        if let Some(tasks) = self.io_tasks.lock().take() {
+            let response_handlers = self.response_handlers.clone();
+            let next_id = AtomicUsize::new(self.next_id.load(SeqCst));
+            let outbound_tx = self.outbound_tx.clone();
+            let executor = self.executor.clone();
+            let mut output_done = self.output_done_rx.lock().take().unwrap();
+            let shutdown_request = Self::request_internal::<request::Shutdown>(
+                &next_id,
+                &response_handlers,
+                &outbound_tx,
+                &executor,
+                (),
+            );
+            let exit = Self::notify_internal::<notification::Exit>(&outbound_tx, ());
+            outbound_tx.close();
+            Some(
+                async move {
+                    log::debug!("language server shutdown started");
+                    shutdown_request.await?;
+                    response_handlers.lock().take();
+                    exit?;
+                    output_done.recv().await;
+                    log::debug!("language server shutdown finished");
+                    drop(tasks);
+                    anyhow::Ok(())
+                }
+                .log_err(),
+            )
+        } else {
+            None
+        }
+    }
+
+    #[must_use]
+    pub fn on_notification<T, F>(&self, f: F) -> Subscription
+    where
+        T: notification::Notification,
+        F: 'static + Send + FnMut(T::Params, AsyncAppContext),
+    {
+        self.on_custom_notification(T::METHOD, f)
+    }
+
+    #[must_use]
+    pub fn on_request<T, F, Fut>(&self, f: F) -> Subscription
+    where
+        T: request::Request,
+        T::Params: 'static + Send,
+        F: 'static + Send + FnMut(T::Params, AsyncAppContext) -> Fut,
+        Fut: 'static + Future<Output = Result<T::Result>> + Send,
+    {
+        self.on_custom_request(T::METHOD, f)
+    }
+
+    #[must_use]
+    pub fn on_io<F>(&self, f: F) -> Subscription
+    where
+        F: 'static + Send + FnMut(IoKind, &str),
+    {
+        let id = self.next_id.fetch_add(1, SeqCst);
+        self.io_handlers.lock().insert(id, Box::new(f));
+        Subscription::Io {
+            id,
+            io_handlers: Some(Arc::downgrade(&self.io_handlers)),
+        }
+    }
+
+    pub fn remove_request_handler<T: request::Request>(&self) {
+        self.notification_handlers.lock().remove(T::METHOD);
+    }
+
+    pub fn remove_notification_handler<T: notification::Notification>(&self) {
+        self.notification_handlers.lock().remove(T::METHOD);
+    }
+
+    pub fn has_notification_handler<T: notification::Notification>(&self) -> bool {
+        self.notification_handlers.lock().contains_key(T::METHOD)
+    }
+
+    #[must_use]
+    pub fn on_custom_notification<Params, F>(&self, method: &'static str, mut f: F) -> Subscription
+    where
+        F: 'static + Send + FnMut(Params, AsyncAppContext),
+        Params: DeserializeOwned,
+    {
+        let prev_handler = self.notification_handlers.lock().insert(
+            method,
+            Box::new(move |_, params, cx| {
+                if let Some(params) = serde_json::from_str(params).log_err() {
+                    f(params, cx);
+                }
+            }),
+        );
+        assert!(
+            prev_handler.is_none(),
+            "registered multiple handlers for the same LSP method"
+        );
+        Subscription::Notification {
+            method,
+            notification_handlers: Some(self.notification_handlers.clone()),
+        }
+    }
+
+    #[must_use]
+    pub fn on_custom_request<Params, Res, Fut, F>(
+        &self,
+        method: &'static str,
+        mut f: F,
+    ) -> Subscription
+    where
+        F: 'static + Send + FnMut(Params, AsyncAppContext) -> Fut,
+        Fut: 'static + Future<Output = Result<Res>> + Send,
+        Params: DeserializeOwned + Send + 'static,
+        Res: Serialize,
+    {
+        let outbound_tx = self.outbound_tx.clone();
+        let prev_handler = self.notification_handlers.lock().insert(
+            method,
+            Box::new(move |id, params, cx| {
+                if let Some(id) = id {
+                    match serde_json::from_str(params) {
+                        Ok(params) => {
+                            let response = f(params, cx.clone());
+                            cx.executor()
+                                .spawn_on_main({
+                                    let outbound_tx = outbound_tx.clone();
+                                    move || async move {
+                                        let response = match response.await {
+                                            Ok(result) => Response {
+                                                jsonrpc: JSON_RPC_VERSION,
+                                                id,
+                                                result: Some(result),
+                                                error: None,
+                                            },
+                                            Err(error) => Response {
+                                                jsonrpc: JSON_RPC_VERSION,
+                                                id,
+                                                result: None,
+                                                error: Some(Error {
+                                                    message: error.to_string(),
+                                                }),
+                                            },
+                                        };
+                                        if let Some(response) =
+                                            serde_json::to_string(&response).log_err()
+                                        {
+                                            outbound_tx.try_send(response).ok();
+                                        }
+                                    }
+                                })
+                                .detach();
+                        }
+
+                        Err(error) => {
+                            log::error!(
+                                "error deserializing {} request: {:?}, message: {:?}",
+                                method,
+                                error,
+                                params
+                            );
+                            let response = AnyResponse {
+                                jsonrpc: JSON_RPC_VERSION,
+                                id,
+                                result: None,
+                                error: Some(Error {
+                                    message: error.to_string(),
+                                }),
+                            };
+                            if let Some(response) = serde_json::to_string(&response).log_err() {
+                                outbound_tx.try_send(response).ok();
+                            }
+                        }
+                    }
+                }
+            }),
+        );
+        assert!(
+            prev_handler.is_none(),
+            "registered multiple handlers for the same LSP method"
+        );
+        Subscription::Notification {
+            method,
+            notification_handlers: Some(self.notification_handlers.clone()),
+        }
+    }
+
+    pub fn name(&self) -> &str {
+        &self.name
+    }
+
+    pub fn capabilities(&self) -> &ServerCapabilities {
+        &self.capabilities
+    }
+
+    pub fn server_id(&self) -> LanguageServerId {
+        self.server_id
+    }
+
+    pub fn root_path(&self) -> &PathBuf {
+        &self.root_path
+    }
+
+    pub fn request<T: request::Request>(
+        &self,
+        params: T::Params,
+    ) -> impl Future<Output = Result<T::Result>>
+    where
+        T::Result: 'static + Send,
+    {
+        Self::request_internal::<T>(
+            &self.next_id,
+            &self.response_handlers,
+            &self.outbound_tx,
+            &self.executor,
+            params,
+        )
+    }
+
+    fn request_internal<T: request::Request>(
+        next_id: &AtomicUsize,
+        response_handlers: &Mutex<Option<HashMap<usize, ResponseHandler>>>,
+        outbound_tx: &channel::Sender<String>,
+        executor: &Executor,
+        params: T::Params,
+    ) -> impl 'static + Future<Output = anyhow::Result<T::Result>>
+    where
+        T::Result: 'static + Send,
+    {
+        let id = next_id.fetch_add(1, SeqCst);
+        let message = serde_json::to_string(&Request {
+            jsonrpc: JSON_RPC_VERSION,
+            id,
+            method: T::METHOD,
+            params,
+        })
+        .unwrap();
+
+        let (tx, rx) = oneshot::channel();
+        let handle_response = response_handlers
+            .lock()
+            .as_mut()
+            .ok_or_else(|| anyhow!("server shut down"))
+            .map(|handlers| {
+                let executor = executor.clone();
+                handlers.insert(
+                    id,
+                    Box::new(move |result| {
+                        executor
+                            .spawn(async move {
+                                let response = match result {
+                                    Ok(response) => serde_json::from_str(&response)
+                                        .context("failed to deserialize response"),
+                                    Err(error) => Err(anyhow!("{}", error.message)),
+                                };
+                                _ = tx.send(response);
+                            })
+                            .detach();
+                    }),
+                );
+            });
+
+        let send = outbound_tx
+            .try_send(message)
+            .context("failed to write to language server's stdin");
+
+        let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
+        let started = Instant::now();
+        async move {
+            handle_response?;
+            send?;
+
+            let method = T::METHOD;
+            futures::select! {
+                response = rx.fuse() => {
+                    let elapsed = started.elapsed();
+                    log::trace!("Took {elapsed:?} to recieve response to {method:?} id {id}");
+                    response?
+                }
+
+                _ = timeout => {
+                    log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}");
+                    anyhow::bail!("LSP request timeout");
+                }
+            }
+        }
+    }
+
+    pub fn notify<T: notification::Notification>(&self, params: T::Params) -> Result<()> {
+        Self::notify_internal::<T>(&self.outbound_tx, params)
+    }
+
+    fn notify_internal<T: notification::Notification>(
+        outbound_tx: &channel::Sender<String>,
+        params: T::Params,
+    ) -> Result<()> {
+        let message = serde_json::to_string(&Notification {
+            jsonrpc: JSON_RPC_VERSION,
+            method: T::METHOD,
+            params,
+        })
+        .unwrap();
+        outbound_tx.try_send(message)?;
+        Ok(())
+    }
+}
+
+impl Drop for LanguageServer {
+    fn drop(&mut self) {
+        if let Some(shutdown) = self.shutdown() {
+            self.executor.spawn(shutdown).detach();
+        }
+    }
+}
+
+impl Subscription {
+    pub fn detach(&mut self) {
+        match self {
+            Subscription::Notification {
+                notification_handlers,
+                ..
+            } => *notification_handlers = None,
+            Subscription::Io { io_handlers, .. } => *io_handlers = None,
+        }
+    }
+}
+
+impl fmt::Display for LanguageServerId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl fmt::Debug for LanguageServer {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("LanguageServer")
+            .field("id", &self.server_id.0)
+            .field("name", &self.name)
+            .finish_non_exhaustive()
+    }
+}
+
+impl Drop for Subscription {
+    fn drop(&mut self) {
+        match self {
+            Subscription::Notification {
+                method,
+                notification_handlers,
+            } => {
+                if let Some(handlers) = notification_handlers {
+                    handlers.lock().remove(method);
+                }
+            }
+            Subscription::Io { id, io_handlers } => {
+                if let Some(io_handlers) = io_handlers.as_ref().and_then(|h| h.upgrade()) {
+                    io_handlers.lock().remove(id);
+                }
+            }
+        }
+    }
+}
+
+#[cfg(any(test, feature = "test-support"))]
+#[derive(Clone)]
+pub struct FakeLanguageServer {
+    pub server: Arc<LanguageServer>,
+    notifications_rx: channel::Receiver<(String, String)>,
+}
+
+#[cfg(any(test, feature = "test-support"))]
+impl LanguageServer {
+    pub fn full_capabilities() -> ServerCapabilities {
+        ServerCapabilities {
+            document_highlight_provider: Some(OneOf::Left(true)),
+            code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
+            document_formatting_provider: Some(OneOf::Left(true)),
+            document_range_formatting_provider: Some(OneOf::Left(true)),
+            definition_provider: Some(OneOf::Left(true)),
+            type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
+            ..Default::default()
+        }
+    }
+
+    pub fn fake(
+        name: String,
+        capabilities: ServerCapabilities,
+        cx: AsyncAppContext,
+    ) -> (Self, FakeLanguageServer) {
+        let (stdin_writer, stdin_reader) = async_pipe::pipe();
+        let (stdout_writer, stdout_reader) = async_pipe::pipe();
+        let (notifications_tx, notifications_rx) = channel::unbounded();
+
+        let server = Self::new_internal(
+            LanguageServerId(0),
+            stdin_writer,
+            stdout_reader,
+            None::<async_pipe::PipeReader>,
+            None,
+            Path::new("/"),
+            None,
+            cx.clone(),
+            |_| {},
+        );
+        let fake = FakeLanguageServer {
+            server: Arc::new(Self::new_internal(
+                LanguageServerId(0),
+                stdout_writer,
+                stdin_reader,
+                None::<async_pipe::PipeReader>,
+                None,
+                Path::new("/"),
+                None,
+                cx,
+                move |msg| {
+                    notifications_tx
+                        .try_send((
+                            msg.method.to_string(),
+                            msg.params
+                                .map(|raw_value| raw_value.get())
+                                .unwrap_or("null")
+                                .to_string(),
+                        ))
+                        .ok();
+                },
+            )),
+            notifications_rx,
+        };
+        fake.handle_request::<request::Initialize, _, _>({
+            let capabilities = capabilities;
+            move |_, _| {
+                let capabilities = capabilities.clone();
+                let name = name.clone();
+                async move {
+                    Ok(InitializeResult {
+                        capabilities,
+                        server_info: Some(ServerInfo {
+                            name,
+                            ..Default::default()
+                        }),
+                    })
+                }
+            }
+        });
+
+        (server, fake)
+    }
+}
+
+#[cfg(any(test, feature = "test-support"))]
+impl FakeLanguageServer {
+    pub fn notify<T: notification::Notification>(&self, params: T::Params) {
+        self.server.notify::<T>(params).ok();
+    }
+
+    pub async fn request<T>(&self, params: T::Params) -> Result<T::Result>
+    where
+        T: request::Request,
+        T::Result: 'static + Send,
+    {
+        self.server.executor.start_waiting();
+        self.server.request::<T>(params).await
+    }
+
+    pub async fn receive_notification<T: notification::Notification>(&mut self) -> T::Params {
+        self.server.executor.start_waiting();
+        self.try_receive_notification::<T>().await.unwrap()
+    }
+
+    pub async fn try_receive_notification<T: notification::Notification>(
+        &mut self,
+    ) -> Option<T::Params> {
+        use futures::StreamExt as _;
+
+        loop {
+            let (method, params) = self.notifications_rx.next().await?;
+            if method == T::METHOD {
+                return Some(serde_json::from_str::<T::Params>(&params).unwrap());
+            } else {
+                log::info!("skipping message in fake language server {:?}", params);
+            }
+        }
+    }
+
+    pub fn handle_request<T, F, Fut>(
+        &self,
+        mut handler: F,
+    ) -> futures::channel::mpsc::UnboundedReceiver<()>
+    where
+        T: 'static + request::Request,
+        T::Params: 'static + Send,
+        F: 'static + Send + FnMut(T::Params, gpui2::AsyncAppContext) -> Fut,
+        Fut: 'static + Send + Future<Output = Result<T::Result>>,
+    {
+        let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded();
+        self.server.remove_request_handler::<T>();
+        self.server
+            .on_request::<T, _, _>(move |params, cx| {
+                let result = handler(params, cx.clone());
+                let responded_tx = responded_tx.clone();
+                async move {
+                    cx.executor().simulate_random_delay().await;
+                    let result = result.await;
+                    responded_tx.unbounded_send(()).ok();
+                    result
+                }
+            })
+            .detach();
+        responded_rx
+    }
+
+    pub fn handle_notification<T, F>(
+        &self,
+        mut handler: F,
+    ) -> futures::channel::mpsc::UnboundedReceiver<()>
+    where
+        T: 'static + notification::Notification,
+        T::Params: 'static + Send,
+        F: 'static + Send + FnMut(T::Params, gpui2::AsyncAppContext),
+    {
+        let (handled_tx, handled_rx) = futures::channel::mpsc::unbounded();
+        self.server.remove_notification_handler::<T>();
+        self.server
+            .on_notification::<T, _>(move |params, cx| {
+                handler(params, cx.clone());
+                handled_tx.unbounded_send(()).ok();
+            })
+            .detach();
+        handled_rx
+    }
+
+    pub fn remove_request_handler<T>(&mut self)
+    where
+        T: 'static + request::Request,
+    {
+        self.server.remove_request_handler::<T>();
+    }
+
+    pub async fn start_progress(&self, token: impl Into<String>) {
+        let token = token.into();
+        self.request::<request::WorkDoneProgressCreate>(WorkDoneProgressCreateParams {
+            token: NumberOrString::String(token.clone()),
+        })
+        .await
+        .unwrap();
+        self.notify::<notification::Progress>(ProgressParams {
+            token: NumberOrString::String(token),
+            value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(Default::default())),
+        });
+    }
+
+    pub fn end_progress(&self, token: impl Into<String>) {
+        self.notify::<notification::Progress>(ProgressParams {
+            token: NumberOrString::String(token.into()),
+            value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())),
+        });
+    }
+}
+
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use gpui::TestAppContext;
+
+//     #[ctor::ctor]
+//     fn init_logger() {
+//         if std::env::var("RUST_LOG").is_ok() {
+//             env_logger::init();
+//         }
+//     }
+
+//     #[gpui::test]
+//     async fn test_fake(cx: &mut TestAppContext) {
+//         let (server, mut fake) =
+//             LanguageServer::fake("the-lsp".to_string(), Default::default(), cx.to_async());
+
+//         let (message_tx, message_rx) = channel::unbounded();
+//         let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
+//         server
+//             .on_notification::<notification::ShowMessage, _>(move |params, _| {
+//                 message_tx.try_send(params).unwrap()
+//             })
+//             .detach();
+//         server
+//             .on_notification::<notification::PublishDiagnostics, _>(move |params, _| {
+//                 diagnostics_tx.try_send(params).unwrap()
+//             })
+//             .detach();
+
+//         let server = server.initialize(None).await.unwrap();
+//         server
+//             .notify::<notification::DidOpenTextDocument>(DidOpenTextDocumentParams {
+//                 text_document: TextDocumentItem::new(
+//                     Url::from_str("file://a/b").unwrap(),
+//                     "rust".to_string(),
+//                     0,
+//                     "".to_string(),
+//                 ),
+//             })
+//             .unwrap();
+//         assert_eq!(
+//             fake.receive_notification::<notification::DidOpenTextDocument>()
+//                 .await
+//                 .text_document
+//                 .uri
+//                 .as_str(),
+//             "file://a/b"
+//         );
+
+//         fake.notify::<notification::ShowMessage>(ShowMessageParams {
+//             typ: MessageType::ERROR,
+//             message: "ok".to_string(),
+//         });
+//         fake.notify::<notification::PublishDiagnostics>(PublishDiagnosticsParams {
+//             uri: Url::from_str("file://b/c").unwrap(),
+//             version: Some(5),
+//             diagnostics: vec![],
+//         });
+//         assert_eq!(message_rx.recv().await.unwrap().message, "ok");
+//         assert_eq!(
+//             diagnostics_rx.recv().await.unwrap().uri.as_str(),
+//             "file://b/c"
+//         );
+
+//         fake.handle_request::<request::Shutdown, _, _>(|_, _| async move { Ok(()) });
+
+//         drop(server);
+//         fake.receive_notification::<notification::Exit>().await;
+//     }
+// }

crates/theme2/Cargo.toml 🔗

@@ -0,0 +1,35 @@
+[package]
+name = "theme2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[features]
+test-support = [
+    "gpui2/test-support",
+    "fs/test-support",
+    "settings2/test-support"
+]
+
+[lib]
+path = "src/theme2.rs"
+doctest = false
+
+[dependencies]
+gpui2 = { path = "../gpui2" }
+fs = { path = "../fs" }
+settings2 = { path = "../settings2" }
+util = { path = "../util" }
+
+anyhow.workspace = true
+indexmap = "1.6.2"
+parking_lot.workspace = true
+serde.workspace = true
+serde_derive.workspace = true
+serde_json.workspace = true
+toml.workspace = true
+
+[dev-dependencies]
+gpui2 = { path = "../gpui2", features = ["test-support"] }
+fs = { path = "../fs", features = ["test-support"] }
+settings2 = { path = "../settings2", features = ["test-support"] }

crates/theme2/src/theme2.rs 🔗

@@ -0,0 +1,21 @@
+use gpui2::HighlightStyle;
+use std::sync::Arc;
+
+pub struct Theme {
+    pub editor: Editor,
+}
+
+pub struct Editor {
+    pub syntax: Arc<SyntaxTheme>,
+}
+
+#[derive(Default)]
+pub struct SyntaxTheme {
+    pub highlights: Vec<(String, HighlightStyle)>,
+}
+
+impl SyntaxTheme {
+    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
+        Self { highlights }
+    }
+}

crates/welcome/Cargo.toml 🔗

@@ -14,7 +14,7 @@ test-support = []
 client = { path = "../client" }
 editor = { path = "../editor" }
 fs = { path = "../fs" }
-fuzzy = { path = "../fuzzy" }
+fuzzy = { path = "../fuzzy2" }
 gpui = { path = "../gpui" }
 db = { path = "../db" }
 install_cli = { path = "../install_cli" }

crates/zed2/src/main.rs 🔗

@@ -108,7 +108,7 @@ fn main() {
         handle_settings_file_changes(user_settings_file_rx, cx);
         // handle_keymap_file_changes(user_keymap_file_rx, cx);
 
-        let client = client2::Client::new(http.clone(), cx);
+        // let client = client2::Client::new(http.clone(), cx);
         // let mut languages = LanguageRegistry::new(login_shell_env_loaded);
         // let copilot_language_server_id = languages.next_language_server_id();
         // languages.set_executor(cx.background().clone());