wip

Cole Miller created

Change summary

crates/dap/src/registry.rs                  |  26 +--
crates/dap_adapters/schemas/JavaScript.json | 140 +++++++-------------
crates/dap_adapters/src/update_schemas.rs   |  22 ++
crates/languages/src/json.rs                |  11 
crates/task/src/adapter_schema.rs           |  18 --
crates/task/src/debug_format.rs             | 150 +++++++++++-----------
crates/task/src/task.rs                     |   2 
7 files changed, 161 insertions(+), 208 deletions(-)

Detailed changes

crates/dap/src/registry.rs 🔗

@@ -4,12 +4,10 @@ use collections::FxHashMap;
 use gpui::{App, Global, SharedString};
 use language::LanguageName;
 use parking_lot::RwLock;
-use task::{
-    AdapterSchema, AdapterSchemas, DebugRequest, DebugScenario, SpawnInTerminal, TaskTemplate,
-};
+use task::{DebugRequest, DebugScenario, SpawnInTerminal, TaskTemplate};
 
 use crate::adapters::{DebugAdapter, DebugAdapterName};
-use std::{collections::BTreeMap, sync::Arc};
+use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
 
 /// Given a user build configuration, locator creates a fill-in debug target ([DebugScenario]) on behalf of the user.
 #[async_trait]
@@ -63,19 +61,13 @@ impl DapRegistry {
             .and_then(|adapter| adapter.adapter_language_name())
     }
 
-    pub async fn adapters_schema(&self) -> task::AdapterSchemas {
-        let mut schemas = AdapterSchemas(vec![]);
-
-        let adapters = self.0.read().adapters.clone();
-
-        for (name, adapter) in adapters.into_iter() {
-            schemas.0.push(AdapterSchema {
-                adapter: name.into(),
-                schema: adapter.dap_schema(),
-            });
-        }
-
-        schemas
+    pub async fn adapter_schemas(&self) -> Vec<(SharedString, Cow<'static, serde_json::Value>)> {
+        self.0
+            .read()
+            .adapters
+            .iter()
+            .map(|(name, adapter)| (name.0.clone(), adapter.dap_schema()))
+            .collect()
     }
 
     pub fn locators(&self) -> FxHashMap<SharedString, Arc<dyn DapLocator>> {

crates/dap_adapters/schemas/JavaScript.json 🔗

@@ -4,14 +4,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-node"
-            ]
+            "const": "pwa-node"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -412,14 +408,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-node"
-            ]
+            "const": "pwa-node"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -757,14 +749,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "node"
-            ]
+            "const": "node"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -1165,14 +1153,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "node"
-            ]
+            "const": "node"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -1510,14 +1494,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "node-terminal"
-            ]
+            "const": "node-terminal"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -1798,14 +1778,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-extensionHost"
-            ]
+            "const": "pwa-extensionHost"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -3098,14 +3074,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "extensionHost"
-            ]
+            "const": "extensionHost"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -4398,14 +4370,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-chrome"
-            ]
+            "const": "pwa-chrome"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -5436,14 +5404,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-chrome"
-            ]
+            "const": "pwa-chrome"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -6428,14 +6392,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "chrome"
-            ]
+            "const": "chrome"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -7466,14 +7426,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "chrome"
-            ]
+            "const": "chrome"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -8458,14 +8414,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-msedge"
-            ]
+            "const": "pwa-msedge"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -9506,14 +9458,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "pwa-msedge"
-            ]
+            "const": "pwa-msedge"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -10510,14 +10458,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "msedge"
-            ]
+            "const": "msedge"
           },
           "request": {
-            "enum": [
-              "launch"
-            ]
+            "const": "launch"
           }
         },
         "required": [
@@ -11558,14 +11502,10 @@
       "if": {
         "properties": {
           "type": {
-            "enum": [
-              "msedge"
-            ]
+            "const": "msedge"
           },
           "request": {
-            "enum": [
-              "attach"
-            ]
+            "const": "attach"
           }
         },
         "required": [
@@ -12557,6 +12497,26 @@
           }
         }
       }
+    },
+    {
+      "properties": {
+        "type": {
+          "enum": [
+            "pwa-node",
+            "node",
+            "node-terminal",
+            "pwa-extensionHost",
+            "extensionHost",
+            "pwa-chrome",
+            "chrome",
+            "pwa-msedge",
+            "msedge"
+          ]
+        }
+      },
+      "required": [
+        "type"
+      ]
     }
   ]
 }

crates/dap_adapters/src/update_schemas.rs 🔗

@@ -88,7 +88,13 @@ fn main() {
 
     let package_json: PackageJson = serde_json::from_value(package_json).unwrap();
 
-    let alternatives = package_json
+    let types = package_json
+        .contributes
+        .debuggers
+        .iter()
+        .map(|debugger| debugger.r#type.clone())
+        .collect::<Vec<_>>();
+    let mut conjuncts = package_json
         .contributes
         .debuggers
         .into_iter()
@@ -109,10 +115,10 @@ fn main() {
                         "if": {
                             "properties": {
                                 "type": {
-                                    "enum": [r#type]
+                                    "const": r#type
                                 },
                                 "request": {
-                                    "enum": [request]
+                                    "const": request
                                 }
                             },
                             "required": ["type", "request"]
@@ -123,8 +129,16 @@ fn main() {
                 .collect::<Vec<_>>()
         })
         .collect::<Vec<_>>();
+    conjuncts.push(json_schema!({
+        "properties": {
+            "type": {
+                "enum": types
+            }
+        },
+        "required": ["type"]
+    }));
     let schema = json_schema!({
-        "allOf": alternatives
+        "allOf": conjuncts
     });
 
     let mut schema = serde_json::to_string_pretty(&schema.to_value()).unwrap();

crates/languages/src/json.rs 🔗

@@ -5,7 +5,7 @@ use async_trait::async_trait;
 use collections::HashMap;
 use dap::DapRegistry;
 use futures::StreamExt;
-use gpui::{App, AsyncApp, Task};
+use gpui::{App, AsyncApp, SharedString, Task};
 use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
 use language::{
     ContextProvider, LanguageRegistry, LanguageToolchainStore, LocalFile as _, LspAdapter,
@@ -23,13 +23,14 @@ use smol::{
 };
 use std::{
     any::Any,
+    borrow::Cow,
     env::consts,
     ffi::OsString,
     path::{Path, PathBuf},
     str::FromStr,
     sync::Arc,
 };
-use task::{AdapterSchemas, TaskTemplate, TaskTemplates, VariableName};
+use task::{TaskTemplate, TaskTemplates, VariableName};
 use util::{ResultExt, archive::extract_zip, fs::remove_matching, maybe, merge_json_value_into};
 
 use crate::PackageJsonData;
@@ -153,7 +154,7 @@ impl JsonLspAdapter {
 
     fn get_workspace_config(
         language_names: Vec<String>,
-        adapter_schemas: AdapterSchemas,
+        adapter_schemas: Vec<(SharedString, Cow<'static, serde_json::Value>)>,
         cx: &mut App,
     ) -> Value {
         let keymap_schema = KeymapFile::generate_json_schema_for_registered_actions(cx);
@@ -167,7 +168,7 @@ impl JsonLspAdapter {
         );
 
         let tasks_schema = task::TaskTemplates::generate_json_schema();
-        let debug_schema = task::DebugTaskFile::generate_json_schema(&adapter_schemas);
+        let debug_schema = task::DebugTaskFile::generate_json_schema(adapter_schemas);
         let snippets_schema = snippet_provider::format::VsSnippetsFile::generate_json_schema();
         let tsconfig_schema = serde_json::Value::from_str(TSCONFIG_SCHEMA).unwrap();
         let package_json_schema = serde_json::Value::from_str(PACKAGE_JSON_SCHEMA).unwrap();
@@ -258,7 +259,7 @@ impl JsonLspAdapter {
 
         let adapter_schemas = cx
             .read_global::<DapRegistry, _>(|dap_registry, _| dap_registry.to_owned())?
-            .adapters_schema()
+            .adapter_schemas()
             .await;
 
         let config = cx.update(|cx| {

crates/task/src/adapter_schema.rs 🔗

@@ -1,18 +0,0 @@
-use std::borrow::Cow;
-
-use gpui::SharedString;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-
-/// JSON schema for a specific adapter
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-pub struct AdapterSchema {
-    /// The adapter name identifier
-    pub adapter: SharedString,
-    /// The JSON schema for this adapter's configuration
-    pub schema: Cow<'static, serde_json::Value>,
-}
-
-#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
-#[serde(transparent)]
-pub struct AdapterSchemas(pub Vec<AdapterSchema>);

crates/task/src/debug_format.rs 🔗

@@ -4,12 +4,32 @@ use gpui::SharedString;
 use log as _;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use std::net::Ipv4Addr;
 use std::path::PathBuf;
-use util::{debug_panic, schemars::add_new_subschema};
-
-use crate::{TaskTemplate, adapter_schema::AdapterSchemas};
-
+use std::{borrow::Cow, net::Ipv4Addr};
+use util::schemars::add_new_subschema;
+
+use crate::TaskTemplate;
+
+// FIXME
+//"tcp_connection": {
+//    "type": "object",
+//    "description": "Optional TCP connection information for connecting to an already running debug adapter",
+//    "properties": {
+//        "port": {
+//            "type": "integer",
+//            "description": "The port that the debug adapter is listening on (default: auto-find open port)"
+//        },
+//        "host": {
+//            "type": "string",
+//            "pattern": "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$",
+//            "description": "The host that the debug adapter is listening to (default: 127.0.0.1)"
+//        },
+//        "timeout": {
+//            "type": "integer",
+//            "description": "The max amount of time in milliseconds to connect to a tcp DAP before returning an error (default: 2000ms)"
+//        }
+//    }
+//}
 /// Represents the host information of the debug adapter
 #[derive(Default, Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
 pub struct TcpArgumentsTemplate {
@@ -281,51 +301,54 @@ pub struct DebugScenario {
 }
 
 /// A group of Debug Tasks defined in a JSON file.
-#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
+#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(transparent)]
 pub struct DebugTaskFile(pub Vec<DebugScenario>);
 
 impl DebugTaskFile {
-    pub fn generate_json_schema(schemas: &AdapterSchemas) -> serde_json::Value {
+    pub fn generate_json_schema(
+        adapter_schemas: Vec<(SharedString, Cow<'static, serde_json::Value>)>,
+    ) -> serde_json::Value {
         let mut generator = schemars::generate::SchemaSettings::draft2019_09().into_generator();
 
-        let mut build_task_value = BuildTaskDefinition::json_schema(&mut generator).to_value();
-
-        if let Some(template_object) = build_task_value
-            .get_mut("anyOf")
-            .and_then(|array| array.as_array_mut())
-            .and_then(|array| array.get_mut(1))
-        {
-            if let Some(properties) = template_object
-                .get_mut("properties")
-                .and_then(|value| value.as_object_mut())
-            {
-                if properties.remove("label").is_none() {
-                    debug_panic!(
-                        "Generated TaskTemplate json schema did not have expected 'label' field. \
-                        Schema of 2nd alternative is: {template_object:?}"
-                    );
-                }
-            }
-
-            if let Some(arr) = template_object
-                .get_mut("required")
-                .and_then(|array| array.as_array_mut())
-            {
-                arr.retain(|v| v.as_str() != Some("label"));
-            }
-        } else {
-            debug_panic!(
-                "Generated TaskTemplate json schema did not match expectations. \
-                Schema is: {build_task_value:?}"
-            );
-        }
-
-        let adapter_conditions = schemas
-            .0
+        // FIXME what is this doing
+        // if let Some(template_object) = build_task_schema
+        //     .get_mut("anyOf")
+        //     .and_then(|array| array.as_array_mut())
+        //     .and_then(|array| array.get_mut(1))
+        // {
+        //     if let Some(properties) = template_object
+        //         .get_mut("properties")
+        //         .and_then(|value| value.as_object_mut())
+        //     {
+        //         if properties.remove("label").is_none() {
+        //             debug_panic!(
+        //                 "Generated TaskTemplate json schema did not have expected 'label' field. \
+        //                 Schema of 2nd alternative is: {template_object:?}"
+        //             );
+        //         }
+        //     }
+
+        //     if let Some(arr) = template_object
+        //         .get_mut("required")
+        //         .and_then(|array| array.as_array_mut())
+        //     {
+        //         arr.retain(|v| v.as_str() != Some("label"));
+        //     }
+        // } else {
+        //     debug_panic!(
+        //         "Generated TaskTemplate json schema did not match expectations. \
+        //         Schema is: {build_task_schema:?}"
+        //     );
+        // }
+
+        let adapter_names = adapter_schemas
+            .iter()
+            .map(|(adapter_name, _)| adapter_name.clone())
+            .collect::<Vec<_>>();
+        let adapter_conditions = adapter_schemas
             .iter()
-            .map(|adapter_schema| {
-                let adapter_name = adapter_schema.adapter.to_string();
+            .map(|(adapter_name, schema)| {
                 add_new_subschema(
                     &mut generator,
                     &format!("{adapter_name}DebugSettings"),
@@ -335,16 +358,23 @@ impl DebugTaskFile {
                                 "adapter": { "const": adapter_name }
                             }
                         },
-                        "then": adapter_schema.schema
+                        "then": schema
                     }),
                 )
             })
             .collect::<Vec<_>>();
 
+        let build_task_schema = BuildTaskDefinition::json_schema(&mut generator).to_value();
         let build_task_definition_ref = add_new_subschema(
             &mut generator,
             BuildTaskDefinition::schema_name().as_ref(),
-            build_task_value,
+            build_task_schema,
+        );
+        let tcp_connection_schema = TcpArgumentsTemplate::json_schema(&mut generator).to_value();
+        let tcp_connection_definition_ref = add_new_subschema(
+            &mut generator,
+            TcpArgumentsTemplate::schema_name().as_ref(),
+            tcp_connection_schema,
         );
 
         let meta_schema = generator
@@ -362,16 +392,10 @@ impl DebugTaskFile {
             "items": {
                 "type": "object",
                 "required": ["adapter", "label"],
-                // TODO: Uncommenting this will cause json-language-server to provide warnings for
-                // unrecognized properties. It should be enabled if/when there's an adapter JSON
-                // schema that's comprehensive. In order to not get warnings for the other schemas,
-                // `additionalProperties` or `unevaluatedProperties` (to handle "allOf" etc style
-                // schema combinations) could be set to `true` for that schema.
-                //
-                // "unevaluatedProperties": false,
+                "unevaluatedProperties": false,
                 "properties": {
                     "adapter": {
-                        "type": "string",
+                        "enum": adapter_names,
                         "description": "The name of the debug adapter"
                     },
                     "label": {
@@ -379,25 +403,7 @@ impl DebugTaskFile {
                         "description": "The name of the debug configuration"
                     },
                     "build": build_task_definition_ref,
-                    "tcp_connection": {
-                        "type": "object",
-                        "description": "Optional TCP connection information for connecting to an already running debug adapter",
-                        "properties": {
-                            "port": {
-                                "type": "integer",
-                                "description": "The port that the debug adapter is listening on (default: auto-find open port)"
-                            },
-                            "host": {
-                                "type": "string",
-                                "pattern": "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$",
-                                "description": "The host that the debug adapter is listening to (default: 127.0.0.1)"
-                            },
-                            "timeout": {
-                                "type": "integer",
-                                "description": "The max amount of time in milliseconds to connect to a tcp DAP before returning an error (default: 2000ms)"
-                            }
-                        }
-                    }
+                    "tcp_connection": tcp_connection_definition_ref,
                 },
                 "allOf": adapter_conditions
             },

crates/task/src/task.rs 🔗

@@ -1,6 +1,5 @@
 //! Baseline interface of Tasks in Zed: all tasks in Zed are intended to use those for implementing their own logic.
 
-mod adapter_schema;
 mod debug_format;
 mod serde_helpers;
 mod shell_builder;
@@ -17,7 +16,6 @@ use std::borrow::Cow;
 use std::path::PathBuf;
 use std::str::FromStr;
 
-pub use adapter_schema::{AdapterSchema, AdapterSchemas};
 pub use debug_format::{
     AttachRequest, BuildTaskDefinition, DebugRequest, DebugScenario, DebugTaskFile, LaunchRequest,
     Request, TcpArgumentsTemplate, ZedDebugConfig,