Merge branch 'main' into load-keymaps

Max Brunsfeld created

Change summary

Cargo.lock                              | 76 ++++++++++++++++---
crates/client/Cargo.toml                |  2 
crates/editor/Cargo.toml                |  2 
crates/gpui/Cargo.toml                  |  4 
crates/journal/Cargo.toml               |  2 
crates/language/Cargo.toml              |  2 
crates/language/src/language.rs         |  8 ++
crates/language/src/tests.rs            | 56 +++++++++++++-
crates/lsp/Cargo.toml                   |  2 
crates/project/Cargo.toml               |  2 
crates/rpc/Cargo.toml                   |  2 
crates/search/Cargo.toml                |  2 
crates/server/Cargo.toml                |  2 
crates/server/k8s/manifest.template.yml |  4 +
crates/server/src/home.rs               |  9 +-
crates/server/src/main.rs               |  8 +
crates/server/src/rpc.rs                | 10 +-
crates/settings/src/settings.rs         | 99 +++++++++++++++++++++++++-
crates/sum_tree/Cargo.toml              |  2 
crates/text/Cargo.toml                  |  2 
crates/text/src/text.rs                 |  5 
crates/theme_selector/Cargo.toml        |  2 
crates/util/Cargo.toml                  |  2 
crates/vim/Cargo.toml                   |  2 
crates/workspace/Cargo.toml             |  2 
crates/zed/Cargo.toml                   |  2 
crates/zed/src/zed.rs                   |  5 +
27 files changed, 260 insertions(+), 56 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -623,7 +623,7 @@ dependencies = [
  "cexpr",
  "clang-sys",
  "clap 2.33.3",
- "env_logger",
+ "env_logger 0.8.3",
  "lazy_static",
  "lazycell",
  "log",
@@ -1643,7 +1643,7 @@ dependencies = [
  "clock",
  "collections",
  "ctor",
- "env_logger",
+ "env_logger 0.8.3",
  "futures",
  "fuzzy",
  "gpui",
@@ -1711,6 +1711,15 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
 
+[[package]]
+name = "env_logger"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "env_logger"
 version = "0.8.3"
@@ -1733,6 +1742,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "erased-serde"
+version = "0.3.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad132dd8d0d0b546348d7d86cb3191aad14b34e5f979781fc005c80d4ac67ffd"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "etagere"
 version = "0.2.4"
@@ -1815,7 +1833,7 @@ version = "0.1.0"
 dependencies = [
  "ctor",
  "editor",
- "env_logger",
+ "env_logger 0.8.3",
  "fuzzy",
  "gpui",
  "postage",
@@ -2244,7 +2262,7 @@ dependencies = [
  "core-text",
  "ctor",
  "dhat",
- "env_logger",
+ "env_logger 0.8.3",
  "etagere",
  "font-kit",
  "foreign-types",
@@ -2661,6 +2679,18 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "json_env_logger"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e2ec540ea0448b187d3a8b4a9f13e75527d06ef76b3a2baa1cd982aecb62ce2"
+dependencies = [
+ "env_logger 0.7.1",
+ "kv-log-macro",
+ "log",
+ "serde_json",
+]
+
 [[package]]
 name = "jwt-simple"
 version = "0.10.1"
@@ -2724,7 +2754,7 @@ dependencies = [
  "clock",
  "collections",
  "ctor",
- "env_logger",
+ "env_logger 0.8.3",
  "futures",
  "fuzzy",
  "gpui",
@@ -2843,11 +2873,12 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
 dependencies = [
  "cfg-if 1.0.0",
+ "serde",
  "value-bag",
 ]
 
@@ -2880,7 +2911,7 @@ dependencies = [
  "async-pipe",
  "collections",
  "ctor",
- "env_logger",
+ "env_logger 0.8.3",
  "futures",
  "gpui",
  "log",
@@ -4352,6 +4383,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "serde_fmt"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2963a69a2b3918c1dc75a45a18bd3fcd1120e31d3f59deb1b2f9b5d5ffb8baa4"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "serde_json"
 version = "1.0.64"
@@ -4998,7 +5038,7 @@ version = "0.1.0"
 dependencies = [
  "arrayvec 0.7.1",
  "ctor",
- "env_logger",
+ "env_logger 0.8.3",
  "log",
  "rand 0.8.3",
 ]
@@ -5030,6 +5070,9 @@ name = "sval"
 version = "1.0.0-alpha.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "svg_fmt"
@@ -5128,7 +5171,7 @@ dependencies = [
  "clock",
  "collections",
  "ctor",
- "env_logger",
+ "env_logger 0.8.3",
  "gpui",
  "lazy_static",
  "log",
@@ -5716,11 +5759,14 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
 
 [[package]]
 name = "value-bag"
-version = "1.0.0-alpha.7"
+version = "1.0.0-alpha.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae"
+checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
 dependencies = [
  "ctor",
+ "erased-serde",
+ "serde",
+ "serde_fmt",
  "sval",
  "version_check",
 ]
@@ -6045,7 +6091,7 @@ dependencies = [
  "dirs 3.0.1",
  "easy-parallel",
  "editor",
- "env_logger",
+ "env_logger 0.8.3",
  "file_finder",
  "fsevent",
  "futures",
@@ -6123,16 +6169,18 @@ dependencies = [
  "ctor",
  "editor",
  "either",
- "env_logger",
+ "env_logger 0.8.3",
  "envy",
  "futures",
  "gpui",
  "handlebars",
  "http-auth-basic",
+ "json_env_logger",
  "jwt-simple",
  "language",
  "lazy_static",
  "lipsum",
+ "log",
  "lsp",
  "oauth2",
  "oauth2-surf",

crates/client/Cargo.toml 🔗

@@ -21,7 +21,7 @@ async-tungstenite = { version = "0.16", features = ["async-tls"] }
 futures = "0.3"
 image = "0.23"
 lazy_static = "1.4.0"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 rand = "0.8.3"

crates/editor/Cargo.toml 🔗

@@ -40,7 +40,7 @@ futures = "0.3"
 indoc = "1.0.4"
 itertools = "0.10"
 lazy_static = "1.4"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 ordered-float = "2.1.1"
 parking_lot = "0.11"
 postage = { version = "0.4", features = ["futures-traits"] }

crates/gpui/Cargo.toml 🔗

@@ -25,7 +25,7 @@ etagere = "0.2"
 futures = "0.3"
 image = "0.23"
 lazy_static = "1.4.0"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 num_cpus = "1.13"
 ordered-float = "2.1.1"
 parking = "2.0.0"
@@ -67,6 +67,6 @@ core-graphics = "0.22.2"
 core-text = "19.2"
 font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "8eaf7a918eafa28b0a37dc759e2e0e7683fa24f1" }
 foreign-types = "0.3"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 metal = "0.21.0"
 objc = "0.2"

crates/journal/Cargo.toml 🔗

@@ -14,4 +14,4 @@ util = { path = "../util" }
 workspace = { path = "../workspace" }
 chrono = "0.4"
 dirs = "4.0"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }

crates/language/Cargo.toml 🔗

@@ -35,7 +35,7 @@ async-broadcast = "0.3.4"
 async-trait = "0.1"
 futures = "0.3"
 lazy_static = "1.4"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 rand = { version = "0.8.3", optional = true }

crates/language/src/language.rs 🔗

@@ -234,6 +234,14 @@ impl LanguageRegistry {
             .cloned()
     }
 
+    pub fn language_names(&self) -> Vec<String> {
+        self.languages
+            .read()
+            .iter()
+            .map(|language| language.name().to_string())
+            .collect()
+    }
+
     pub fn select_language(&self, path: impl AsRef<Path>) -> Option<Arc<Language>> {
         let path = path.as_ref();
         let filename = path.file_name().and_then(|name| name.to_str());

crates/language/src/tests.rs 🔗

@@ -821,7 +821,10 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
             }
             50..=59 if replica_ids.len() < max_peers => {
                 let old_buffer = buffer.read(cx).to_proto();
-                let new_replica_id = replica_ids.len() as ReplicaId;
+                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,
@@ -830,6 +833,11 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
                 new_buffer = Some(cx.add_model(|cx| {
                     let mut new_buffer =
                         Buffer::from_proto(new_replica_id, old_buffer, None, 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, _| {
@@ -843,8 +851,33 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
                     .detach();
                     new_buffer
                 }));
-                replica_ids.push(new_replica_id);
                 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| {
@@ -861,9 +894,11 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
                     .map(|op| proto::deserialize_operation(op).unwrap());
                 if ops.len() > 0 {
                     log::info!(
-                        "peer {} applying {} ops from the network.",
+                        "peer {} (version: {:?}) applying {} ops from the network. {:?}",
                         replica_id,
-                        ops.len()
+                        buffer.read(cx).version(),
+                        ops.len(),
+                        ops
                     );
                     buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
                 }
@@ -886,6 +921,12 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
     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(),
@@ -915,7 +956,12 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
             .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);
+        assert_eq!(
+            actual_remote_selections,
+            expected_remote_selections,
+            "Replica {} remote selections != expected selections",
+            buffer.replica_id()
+        );
     }
 }
 

crates/lsp/Cargo.toml 🔗

@@ -17,7 +17,7 @@ util = { path = "../util" }
 anyhow = "1.0"
 async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true }
 futures = "0.3"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 lsp-types = "0.91"
 parking_lot = "0.11"
 postage = { version = "0.4.1", features = ["futures-traits"] }

crates/project/Cargo.toml 🔗

@@ -35,7 +35,7 @@ futures = "0.3"
 ignore = "0.4"
 lazy_static = "1.4.0"
 libc = "0.2"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 rand = "0.8.3"

crates/rpc/Cargo.toml 🔗

@@ -21,7 +21,7 @@ async-lock = "2.4"
 async-tungstenite = "0.16"
 base64 = "0.13"
 futures = "0.3"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 prost = "0.8"
 rand = "0.8"

crates/search/Cargo.toml 🔗

@@ -18,7 +18,7 @@ theme = { path = "../theme" }
 util = { path = "../util" }
 workspace = { path = "../workspace" }
 anyhow = "1.0"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1", features = ["derive"] }
 

crates/server/Cargo.toml 🔗

@@ -28,8 +28,10 @@ envy = "0.4.2"
 futures = "0.3"
 handlebars = "3.5"
 http-auth-basic = "0.1.3"
+json_env_logger = "0.1"
 jwt-simple = "0.10.0"
 lipsum = { version = "0.8", optional = true }
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 oauth2 = { version = "4.0.0", default_features = false }
 oauth2-surf = "0.1.1"
 parking_lot = "0.11.1"

crates/server/k8s/manifest.template.yml 🔗

@@ -81,6 +81,10 @@ spec:
                 secretKeyRef:
                   name: api
                   key: token
+            - name: LOG_JSON
+              value: "1"
+            - name: RUST_LOG
+              value: "trace"
           securityContext:
             capabilities:
               # FIXME - Switch to the more restrictive `PERFMON` capability.

crates/server/src/home.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{AppState, Request, RequestExt as _};
-use serde::Deserialize;
+use log::as_serde;
+use serde::{Deserialize, Serialize};
 use std::sync::Arc;
-use tide::{http::mime, log, Server};
+use tide::{http::mime, Server};
 
 pub fn add_routes(app: &mut Server<Arc<AppState>>) {
     app.at("/").get(get_home);
@@ -18,7 +19,7 @@ async fn get_home(mut request: Request) -> tide::Result {
 }
 
 async fn post_signup(mut request: Request) -> tide::Result {
-    #[derive(Debug, Deserialize)]
+    #[derive(Debug, Deserialize, Serialize)]
     struct Form {
         github_login: String,
         email_address: String,
@@ -38,7 +39,7 @@ async fn post_signup(mut request: Request) -> tide::Result {
         .map(str::to_string)
         .unwrap_or(form.github_login);
 
-    log::info!("Signup submitted: {:?}", form);
+    log::info!(form = as_serde!(form); "signup submitted");
 
     // Save signup in the database
     request

crates/server/src/main.rs 🔗

@@ -27,7 +27,7 @@ use rust_embed::RustEmbed;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
 use surf::http::cookies::SameSite;
-use tide::{log, sessions::SessionMiddleware};
+use tide::sessions::SessionMiddleware;
 use tide_compress::CompressMiddleware;
 
 type Request = tide::Request<Arc<AppState>>;
@@ -138,7 +138,11 @@ struct LayoutData {
 
 #[async_std::main]
 async fn main() -> tide::Result<()> {
-    log::start();
+    if std::env::var("LOG_JSON").is_ok() {
+        json_env_logger::init();
+    } else {
+        tide::log::start();
+    }
 
     if let Err(error) = env::load_dotenv() {
         log::error!(

crates/server/src/rpc.rs 🔗

@@ -11,6 +11,7 @@ use async_std::task;
 use async_tungstenite::{tungstenite::protocol::Role, WebSocketStream};
 use collections::{HashMap, HashSet};
 use futures::{channel::mpsc, future::BoxFuture, FutureExt, SinkExt, StreamExt};
+use log::{as_debug, as_display};
 use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
 use rpc::{
     proto::{self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, RequestMessage},
@@ -25,7 +26,6 @@ use std::{
 };
 use store::{Store, Worktree};
 use surf::StatusCode;
-use tide::log;
 use tide::{
     http::headers::{HeaderName, CONNECTION, UPGRADE},
     Request, Response,
@@ -218,16 +218,16 @@ impl Server {
                         if let Some(message) = message {
                             let start_time = Instant::now();
                             let type_name = message.payload_type_name();
-                            log::info!("rpc message received. connection:{}, type:{}", connection_id, type_name);
+                            log::info!(connection_id = connection_id.0, type_name = type_name; "rpc message received");
                             if let Some(handler) = this.handlers.get(&message.payload_type_id()) {
                                 let notifications = this.notifications.clone();
                                 let is_background = message.is_background();
                                 let handle_message = (handler)(this.clone(), message);
                                 let handle_message = async move {
                                     if let Err(err) = handle_message.await {
-                                        log::error!("rpc message error. connection:{}, type:{}, error:{:?}", connection_id, type_name, err);
+                                        log::error!(connection_id = connection_id.0, type = type_name, error = as_display!(err); "rpc message error");
                                     } else {
-                                        log::info!("rpc message handled. connection:{}, type:{}, duration:{:?}", connection_id, type_name, start_time.elapsed());
+                                        log::info!(connection_id = connection_id.0, type = type_name, duration = as_debug!(start_time.elapsed()); "rpc message handled");
                                     }
                                     if let Some(mut notifications) = notifications {
                                         let _ = notifications.send(()).await;
@@ -242,7 +242,7 @@ impl Server {
                                 log::warn!("unhandled message: {}", type_name);
                             }
                         } else {
-                            log::info!("rpc connection closed {:?}", addr);
+                            log::info!(address = as_debug!(addr); "rpc connection closed");
                             break;
                         }
                     }

crates/settings/src/settings.rs 🔗

@@ -2,8 +2,15 @@ pub mod keymap_file;
 
 use anyhow::Result;
 use gpui::font_cache::{FamilyId, FontCache};
-use schemars::{schema_for, JsonSchema};
+use schemars::{
+    gen::{SchemaGenerator, SchemaSettings},
+    schema::{
+        InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation,
+    },
+    JsonSchema,
+};
 use serde::Deserialize;
+use serde_json::Value;
 use std::{collections::HashMap, sync::Arc};
 use theme::{Theme, ThemeRegistry};
 use util::ResultExt as _;
@@ -69,8 +76,88 @@ impl Settings {
         })
     }
 
-    pub fn file_json_schema() -> serde_json::Value {
-        serde_json::to_value(schema_for!(SettingsFileContent)).unwrap()
+    pub fn file_json_schema(
+        theme_names: Vec<String>,
+        language_names: Vec<String>,
+    ) -> serde_json::Value {
+        let settings = SchemaSettings::draft07().with(|settings| {
+            settings.option_add_null_type = false;
+        });
+        let generator = SchemaGenerator::new(settings);
+        let mut root_schema = generator.into_root_schema_for::<SettingsFileContent>();
+
+        // Construct theme names reference type
+        let theme_names = theme_names
+            .into_iter()
+            .map(|name| Value::String(name))
+            .collect();
+        let theme_names_schema = Schema::Object(SchemaObject {
+            instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
+            enum_values: Some(theme_names),
+            ..Default::default()
+        });
+        root_schema
+            .definitions
+            .insert("ThemeName".to_owned(), theme_names_schema);
+
+        // Construct language overrides reference type
+        let language_override_schema_reference = Schema::Object(SchemaObject {
+            reference: Some("#/definitions/LanguageOverride".to_owned()),
+            ..Default::default()
+        });
+        let language_overrides_properties = language_names
+            .into_iter()
+            .map(|name| {
+                (
+                    name,
+                    Schema::Object(SchemaObject {
+                        subschemas: Some(Box::new(SubschemaValidation {
+                            all_of: Some(vec![language_override_schema_reference.clone()]),
+                            ..Default::default()
+                        })),
+                        ..Default::default()
+                    }),
+                )
+            })
+            .collect();
+        let language_overrides_schema = Schema::Object(SchemaObject {
+            instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
+            object: Some(Box::new(ObjectValidation {
+                properties: language_overrides_properties,
+                ..Default::default()
+            })),
+            ..Default::default()
+        });
+        root_schema
+            .definitions
+            .insert("LanguageOverrides".to_owned(), language_overrides_schema);
+
+        // Modify theme property to use new theme reference type
+        let settings_file_schema = root_schema.schema.object.as_mut().unwrap();
+        let language_overrides_schema_reference = Schema::Object(SchemaObject {
+            reference: Some("#/definitions/ThemeName".to_owned()),
+            ..Default::default()
+        });
+        settings_file_schema.properties.insert(
+            "theme".to_owned(),
+            Schema::Object(SchemaObject {
+                subschemas: Some(Box::new(SubschemaValidation {
+                    all_of: Some(vec![language_overrides_schema_reference]),
+                    ..Default::default()
+                })),
+                ..Default::default()
+            }),
+        );
+
+        // Modify language_overrides property to use LanguageOverrides reference
+        settings_file_schema.properties.insert(
+            "language_overrides".to_owned(),
+            Schema::Object(SchemaObject {
+                reference: Some("#/definitions/LanguageOverrides".to_owned()),
+                ..Default::default()
+            }),
+        );
+        serde_json::to_value(root_schema).unwrap()
     }
 
     pub fn with_overrides(
@@ -130,7 +217,7 @@ impl Settings {
             }
         }
         if let Some(value) = &data.theme {
-            if let Some(theme) = theme_registry.get(value).log_err() {
+            if let Some(theme) = theme_registry.get(&value.to_string()).log_err() {
                 self.theme = theme;
             }
         }
@@ -144,10 +231,10 @@ impl Settings {
             data.editor.preferred_line_length,
         );
 
-        for (language_name, settings) in &data.language_overrides {
+        for (language_name, settings) in data.language_overrides.clone().into_iter() {
             let target = self
                 .language_overrides
-                .entry(language_name.clone())
+                .entry(language_name.into())
                 .or_default();
 
             merge_option(&mut target.tab_size, settings.tab_size);

crates/sum_tree/Cargo.toml 🔗

@@ -9,7 +9,7 @@ doctest = false
 
 [dependencies]
 arrayvec = "0.7.1"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 
 [dev-dependencies]
 ctor = "0.1"

crates/text/Cargo.toml 🔗

@@ -17,7 +17,7 @@ sum_tree = { path = "../sum_tree" }
 anyhow = "1.0.38"
 arrayvec = "0.7.1"
 lazy_static = "1.4"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 rand = { version = "0.8.3", optional = true }

crates/text/src/text.rs 🔗

@@ -826,6 +826,8 @@ impl Buffer {
                         edit.timestamp,
                     );
                     self.snapshot.version.observe(edit.timestamp.local());
+                    self.local_clock.observe(edit.timestamp.local());
+                    self.lamport_clock.observe(edit.timestamp.lamport());
                     self.resolve_edit(edit.timestamp.local());
                 }
             }
@@ -836,6 +838,7 @@ impl Buffer {
                 if !self.version.observed(undo.id) {
                     self.apply_undo(&undo)?;
                     self.snapshot.version.observe(undo.id);
+                    self.local_clock.observe(undo.id);
                     self.lamport_clock.observe(lamport_timestamp);
                 }
             }
@@ -1033,8 +1036,6 @@ impl Buffer {
         self.snapshot.visible_text = visible_text;
         self.snapshot.deleted_text = deleted_text;
         self.snapshot.insertions.edit(new_insertions, &());
-        self.local_clock.observe(timestamp.local());
-        self.lamport_clock.observe(timestamp.lamport());
         self.subscriptions.publish_mut(&edits);
     }
 

crates/theme_selector/Cargo.toml 🔗

@@ -14,7 +14,7 @@ gpui = { path = "../gpui" }
 theme = { path = "../theme" }
 settings = { path = "../settings" }
 workspace = { path = "../workspace" }
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 smol = "1.2.5"

crates/util/Cargo.toml 🔗

@@ -12,7 +12,7 @@ test-support = ["rand", "serde_json", "tempdir"]
 [dependencies]
 anyhow = "1.0.38"
 futures = "0.3"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 rand = { version = "0.8", optional = true }
 surf = "2.2"
 tempdir = { version = "0.3.7", optional = true }

crates/vim/Cargo.toml 🔗

@@ -16,7 +16,7 @@ language = { path = "../language" }
 serde = { version = "1", features = ["derive"] }
 settings = { path = "../settings" }
 workspace = { path = "../workspace" }
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 
 [dev-dependencies]
 indoc = "1.0.4"

crates/workspace/Cargo.toml 🔗

@@ -22,7 +22,7 @@ theme = { path = "../theme" }
 util = { path = "../util" }
 anyhow = "1.0.38"
 futures = "0.3"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 parking_lot = "0.11.1"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 serde = { version = "1", features = ["derive", "rc"] }

crates/zed/Cargo.toml 🔗

@@ -76,7 +76,7 @@ image = "0.23"
 indexmap = "1.6.2"
 lazy_static = "1.4.0"
 libc = "0.2"
-log = "0.4"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 log-panics = { version = "2.0", features = ["with-backtrace"] }
 num_cpus = "1.13.0"
 parking_lot = "0.11.1"

crates/zed/src/zed.rs 🔗

@@ -140,13 +140,16 @@ pub fn build_workspace(
     let mut workspace = Workspace::new(&workspace_params, cx);
     let project = workspace.project().clone();
 
+    let theme_names = app_state.themes.list().collect();
+    let language_names = app_state.languages.language_names();
+
     project.update(cx, |project, _| {
         project.set_language_server_settings(serde_json::json!({
             "json": {
                 "schemas": [
                     {
                         "fileMatch": "**/.zed/settings.json",
-                        "schema": Settings::file_json_schema(),
+                        "schema": Settings::file_json_schema(theme_names, language_names),
                     }
                 ]
             }