Detailed changes
@@ -6956,6 +6956,7 @@ dependencies = [
"serde_json",
"settings",
"smol",
+ "snippet_provider",
"task",
"text",
"theme",
@@ -12059,6 +12060,7 @@ dependencies = [
"gpui",
"parking_lot",
"paths",
+ "schemars",
"serde",
"serde_json",
"snippet",
@@ -60,6 +60,7 @@ serde.workspace = true
serde_json.workspace = true
settings.workspace = true
smol.workspace = true
+snippet_provider.workspace = true
task.workspace = true
toml.workspace = true
tree-sitter = { workspace = true, optional = true }
@@ -85,6 +85,7 @@ impl JsonLspAdapter {
cx,
);
let tasks_schema = task::TaskTemplates::generate_json_schema();
+ 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();
@@ -125,8 +126,17 @@ impl JsonLspAdapter {
paths::local_tasks_file_relative_path()
],
"schema": tasks_schema,
+ },
+ {
+ "fileMatch": [
+ schema_file_match(
+ paths::snippets_dir()
+ .join("*.json")
+ .as_path()
+ )
+ ],
+ "schema": snippets_schema,
}
-
]
}
})
@@ -189,6 +189,12 @@ pub fn themes_dir() -> &'static PathBuf {
THEMES_DIR.get_or_init(|| config_dir().join("themes"))
}
+/// Returns the path to the snippets directory.
+pub fn snippets_dir() -> &'static PathBuf {
+ static SNIPPETS_DIR: OnceLock<PathBuf> = OnceLock::new();
+ SNIPPETS_DIR.get_or_init(|| config_dir().join("snippets"))
+}
+
/// Returns the path to the contexts directory.
///
/// This is where the saved contexts from the Assistant are stored.
@@ -21,3 +21,4 @@ serde.workspace = true
serde_json.workspace = true
snippet.workspace = true
util.workspace = true
+schemars.workspace = true
@@ -1,13 +1,47 @@
use collections::HashMap;
+use schemars::{
+ gen::SchemaSettings,
+ schema::{ObjectValidation, Schema, SchemaObject},
+ JsonSchema,
+};
use serde::Deserialize;
+use serde_json::Value;
#[derive(Deserialize)]
-pub(crate) struct VSSnippetsFile {
+pub struct VSSnippetsFile {
#[serde(flatten)]
pub(crate) snippets: HashMap<String, VSCodeSnippet>,
}
-#[derive(Deserialize)]
+impl VSSnippetsFile {
+ pub fn generate_json_schema() -> Value {
+ let schema = SchemaSettings::draft07()
+ .with(|settings| settings.option_add_null_type = false)
+ .into_generator()
+ .into_root_schema_for::<Self>();
+
+ serde_json::to_value(schema).unwrap()
+ }
+}
+
+impl JsonSchema for VSSnippetsFile {
+ fn schema_name() -> String {
+ "VSSnippetsFile".into()
+ }
+
+ fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
+ SchemaObject {
+ object: Some(Box::new(ObjectValidation {
+ additional_properties: Some(Box::new(gen.subschema_for::<VSCodeSnippet>())),
+ ..Default::default()
+ })),
+ ..Default::default()
+ }
+ .into()
+ }
+}
+
+#[derive(Deserialize, JsonSchema)]
#[serde(untagged)]
pub(crate) enum ListOrDirect {
Single(String),
@@ -36,9 +70,14 @@ impl std::fmt::Display for ListOrDirect {
}
}
-#[derive(Deserialize)]
+#[derive(Deserialize, JsonSchema)]
pub(crate) struct VSCodeSnippet {
+ /// The snippet prefix used to decide whether a completion menu should be shown.
pub(crate) prefix: Option<ListOrDirect>,
+
+ /// The snippet content. Use `$1` and `${1:defaultText}` to define cursor positions and `$0` for final cursor position.
pub(crate) body: ListOrDirect,
+
+ /// The snippet description displayed inside the completion menu.
pub(crate) description: Option<ListOrDirect>,
}
@@ -1,5 +1,5 @@
mod extension_snippet;
-mod format;
+pub mod format;
mod registry;
use std::{