From fb3ef0d140156511e4880d689a01ae60f5b89fcf Mon Sep 17 00:00:00 2001 From: Tim Havlicek Date: Thu, 27 Jun 2024 11:12:02 +0200 Subject: [PATCH] Add separate JSONC language (#12655) Resolves https://github.com/zed-industries/extensions/issues/860 and https://github.com/zed-industries/zed/issues/10921, also https://github.com/biomejs/biome-zed/issues/11. ### Problem: When opening .json files, zed allows comments by default in the JSON language, which can cause some problems. For example, language-servers also get "json" as the language, which may show errors for those comments. image ### Solution: This PR adds a JSONC language. image This allows for more specific configuration for language servers. Also any json file can be set explicitly to be JSONC using the file_types setting: ```jsonc { "file_types": { // set all .json files to be seen as JSONC "JSONC": ["*.json"] } } ``` Release Notes: - N/A --- assets/settings/default.json | 4 +++- crates/language/src/language_registry.rs | 2 +- crates/language/src/language_settings.rs | 20 ++++++++++++++++++++ crates/languages/src/json.rs | 7 ++++++- crates/languages/src/jsonc/brackets.scm | 3 +++ crates/languages/src/jsonc/config.toml | 12 ++++++++++++ crates/languages/src/jsonc/embedding.scm | 14 ++++++++++++++ crates/languages/src/jsonc/highlights.scm | 21 +++++++++++++++++++++ crates/languages/src/jsonc/indents.scm | 2 ++ crates/languages/src/jsonc/outline.scm | 2 ++ crates/languages/src/jsonc/overrides.scm | 1 + crates/languages/src/jsonc/redactions.scm | 4 ++++ crates/languages/src/lib.rs | 9 +++++++++ 13 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 crates/languages/src/jsonc/brackets.scm create mode 100644 crates/languages/src/jsonc/config.toml create mode 100644 crates/languages/src/jsonc/embedding.scm create mode 100644 crates/languages/src/jsonc/highlights.scm create mode 100644 crates/languages/src/jsonc/indents.scm create mode 100644 crates/languages/src/jsonc/outline.scm create mode 100644 crates/languages/src/jsonc/overrides.scm create mode 100644 crates/languages/src/jsonc/redactions.scm diff --git a/assets/settings/default.json b/assets/settings/default.json index a3d95591dc..caaae18841 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -688,7 +688,9 @@ // "TOML": ["Embargo.lock"] // } // - "file_types": {}, + "file_types": { + "JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json"] + }, // The extensions that Zed should automatically install on startup. // // If you don't want any of these extensions, add this field to your settings diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 44454b562c..196df43e2c 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -511,7 +511,7 @@ impl LanguageRegistry { ) -> impl Future>> { let filename = path.file_name().and_then(|name| name.to_str()); let extension = path.extension_or_hidden_file_name(); - let path_suffixes = [extension, filename]; + let path_suffixes = [extension, filename, path.to_str()]; let empty = GlobSet::empty(); let rx = self.get_or_load_language(move |language_name, config| { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 345d491244..dff00d2b66 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -662,6 +662,17 @@ impl settings::Settings for AllLanguageSettings { .ok_or_else(Self::missing_default)?; let mut file_types: HashMap, GlobSet> = HashMap::default(); + + for (language, suffixes) in &default_value.file_types { + let mut builder = GlobSetBuilder::new(); + + for suffix in suffixes { + builder.add(Glob::new(suffix)?); + } + + file_types.insert(language.clone(), builder.build()?); + } + for user_settings in sources.customizations() { if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) { copilot_enabled = Some(copilot); @@ -701,6 +712,15 @@ impl settings::Settings for AllLanguageSettings { for (language, suffixes) in &user_settings.file_types { let mut builder = GlobSetBuilder::new(); + let default_value = default_value.file_types.get(&language.clone()); + + // Merge the default value with the user's value. + if let Some(suffixes) = default_value { + for suffix in suffixes { + builder.add(Glob::new(suffix)?); + } + } + for suffix in suffixes { builder.add(Glob::new(suffix)?); } diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 7877ab86da..ac869232b6 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -213,7 +213,12 @@ impl LspAdapter for JsonLspAdapter { } fn language_ids(&self) -> HashMap { - [("JSON".into(), "jsonc".into())].into_iter().collect() + [ + ("JSON".into(), "json".into()), + ("JSONC".into(), "jsonc".into()), + ] + .into_iter() + .collect() } } diff --git a/crates/languages/src/jsonc/brackets.scm b/crates/languages/src/jsonc/brackets.scm new file mode 100644 index 0000000000..9e8c9cd93c --- /dev/null +++ b/crates/languages/src/jsonc/brackets.scm @@ -0,0 +1,3 @@ +("[" @open "]" @close) +("{" @open "}" @close) +("\"" @open "\"" @close) diff --git a/crates/languages/src/jsonc/config.toml b/crates/languages/src/jsonc/config.toml new file mode 100644 index 0000000000..fe62764b27 --- /dev/null +++ b/crates/languages/src/jsonc/config.toml @@ -0,0 +1,12 @@ +name = "JSONC" +grammar = "jsonc" +path_suffixes = ["jsonc"] +line_comments = ["// "] +autoclose_before = ",]}" +brackets = [ + { start = "{", end = "}", close = true, newline = true }, + { start = "[", end = "]", close = true, newline = true }, + { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, +] +tab_size = 2 +prettier_parser_name = "jsonc" diff --git a/crates/languages/src/jsonc/embedding.scm b/crates/languages/src/jsonc/embedding.scm new file mode 100644 index 0000000000..fa286e3880 --- /dev/null +++ b/crates/languages/src/jsonc/embedding.scm @@ -0,0 +1,14 @@ +; Only produce one embedding for the entire file. +(document) @item + +; Collapse arrays, except for the first object. +(array + "[" @keep + . + (object)? @keep + "]" @keep) @collapse + +; Collapse string values (but not keys). +(pair value: (string + "\"" @keep + "\"" @keep) @collapse) diff --git a/crates/languages/src/jsonc/highlights.scm b/crates/languages/src/jsonc/highlights.scm new file mode 100644 index 0000000000..7116805109 --- /dev/null +++ b/crates/languages/src/jsonc/highlights.scm @@ -0,0 +1,21 @@ +(comment) @comment + +(string) @string + +(pair + key: (string) @property.json_key) + +(number) @number + +[ + (true) + (false) + (null) +] @constant + +[ + "{" + "}" + "[" + "]" +] @punctuation.bracket diff --git a/crates/languages/src/jsonc/indents.scm b/crates/languages/src/jsonc/indents.scm new file mode 100644 index 0000000000..b7b2a2e767 --- /dev/null +++ b/crates/languages/src/jsonc/indents.scm @@ -0,0 +1,2 @@ +(array "]" @end) @indent +(object "}" @end) @indent diff --git a/crates/languages/src/jsonc/outline.scm b/crates/languages/src/jsonc/outline.scm new file mode 100644 index 0000000000..43e2743478 --- /dev/null +++ b/crates/languages/src/jsonc/outline.scm @@ -0,0 +1,2 @@ +(pair + key: (string (string_content) @name)) @item diff --git a/crates/languages/src/jsonc/overrides.scm b/crates/languages/src/jsonc/overrides.scm new file mode 100644 index 0000000000..cc966ad4c1 --- /dev/null +++ b/crates/languages/src/jsonc/overrides.scm @@ -0,0 +1 @@ +(string) @string diff --git a/crates/languages/src/jsonc/redactions.scm b/crates/languages/src/jsonc/redactions.scm new file mode 100644 index 0000000000..7359637244 --- /dev/null +++ b/crates/languages/src/jsonc/redactions.scm @@ -0,0 +1,4 @@ +(pair value: (number) @redact) +(pair value: (string) @redact) +(array (number) @redact) +(array (string) @redact) diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 205644fbba..23ffec6559 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -45,6 +45,7 @@ pub fn init( ("gowork", tree_sitter_gowork::language()), ("jsdoc", tree_sitter_jsdoc::language()), ("json", tree_sitter_json::language()), + ("jsonc", tree_sitter_json::language()), ("markdown", tree_sitter_markdown::language()), ("proto", tree_sitter_proto::language()), ("python", tree_sitter_python::language()), @@ -126,6 +127,14 @@ pub fn init( ], json_task_context() ); + language!( + "jsonc", + vec![Arc::new(json::JsonLspAdapter::new( + node_runtime.clone(), + languages.clone(), + ))], + json_task_context() + ); language!("markdown"); language!( "python",