Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 16 additions & 5 deletions crates/extension_host/src/extension_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@ impl ExtensionStore {
Path::new(language.extension.as_ref()),
language.path.as_path(),
]);
let language_name = language_name.clone();
self.proxy.register_language(
language_name.clone(),
language.grammar.clone(),
Expand All @@ -1314,7 +1315,7 @@ impl ExtensionStore {
Arc::new(move || {
let config = std::fs::read_to_string(language_path.join("config.toml"))?;
let config: LanguageConfig = ::toml::from_str(&config)?;
let queries = load_plugin_queries(&language_path);
let queries = load_plugin_queries(&language_name, &language_path);
let context_provider =
std::fs::read_to_string(language_path.join("tasks.json"))
.ok()
Expand Down Expand Up @@ -1879,23 +1880,34 @@ impl ExtensionStore {
}
}

fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
fn load_plugin_queries(language_name: &LanguageName, root_path: &Path) -> LanguageQueries {
let mut result = LanguageQueries::default();
load_query_dir(&mut result, root_path);
load_query_dir(
&mut result,
&paths::languages_config_dir().join(language_name.as_ref()),
);
result
}

fn load_query_dir(result: &mut LanguageQueries, root_path: &Path) {
if let Some(entries) = std::fs::read_dir(root_path).log_err() {
for entry in entries {
let Some(entry) = entry.log_err() else {
continue;
};
let path = entry.path();
log::debug!("Found language dir at {path:?}");
if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
if !remainder.ends_with(".scm") {
continue;
}
for (name, query) in QUERY_FILENAME_PREFIXES {
if remainder.starts_with(name) {
if let Some(contents) = std::fs::read_to_string(&path).log_err() {
match query(&mut result) {
None => *query(&mut result) = Some(contents.into()),
log::debug!("Found query at {path:?}");
match query(result) {
None => *query(result) = Some(contents.into()),
Some(r) => r.to_mut().push_str(contents.as_ref()),
}
}
Expand All @@ -1905,5 +1917,4 @@ fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
}
}
}
result
}
1 change: 1 addition & 0 deletions crates/languages/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ log.workspace = true
lsp.workspace = true
node_runtime.workspace = true
parking_lot.workspace = true
paths.workspace = true
pet-conda.workspace = true
pet-core.workspace = true
pet-fs.workspace = true
Expand Down
35 changes: 35 additions & 0 deletions crates/languages/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,5 +435,40 @@ fn load_queries(name: &str) -> LanguageQueries {
}
}
}

// TODO: dedupe this logic with extension_host? Also, should there be some kind of
// reload logic to pick up changes in the external queries for builtin langs?
let root_path = paths::languages_config_dir().join(name);
let config_dir = std::fs::read_dir(&root_path)
.context(format!("reading directory: {root_path:?}"))
.log_with_level(log::Level::Debug);

if let Some(entries) = config_dir {
log::debug!("adding queries from {root_path:?}");
for entry in entries {
let Some(entry) = entry.log_err() else {
continue;
};
let path = entry.path();
if let Some(remainder) = path.strip_prefix(&root_path).ok().and_then(|p| p.to_str()) {
if !remainder.ends_with(".scm") {
continue;
}
log::debug!("Found query at {path:?}");
for (name, query, _) in QUERY_FILENAME_PREFIXES {
if remainder.starts_with(name) {
if let Some(contents) = std::fs::read_to_string(&path).log_err() {
match query(&mut result) {
None => *query(&mut result) = Some(contents.into()),
Some(r) => r.to_mut().push_str(&contents),
}
}
break;
}
}
}
}
}

result
}
6 changes: 6 additions & 0 deletions crates/paths/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ pub fn snippets_dir() -> &'static PathBuf {
SNIPPETS_DIR.get_or_init(|| config_dir().join("snippets"))
}

/// Returns the path to the user languages directory.
pub fn languages_config_dir() -> &'static PathBuf {
static LANGUAGES_CONFIG_DIR: OnceLock<PathBuf> = OnceLock::new();
LANGUAGES_CONFIG_DIR.get_or_init(|| config_dir().join("languages"))
}

/// Returns the path to the contexts directory.
///
/// This is where the saved contexts from the Assistant are stored.
Expand Down
5 changes: 5 additions & 0 deletions docs/src/extensions/languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ several features:
The following sections elaborate on how [Tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/index.html) enable these
features in Zed, using [JSON syntax](https://www.json.org/json-en.html) as a guiding example.

Note that user-defined queries may also be defined in `~/.config/zed/languages` (macOS and Linux) or `%USERPROFILE%\AppData\Roaming\Zed\languages\` (Windows).
If found, the queries will be appended to builtin queries or queries from extensions.

For example, to add a new language injection to Rust, create a query file at `~/.config/zed/languages/rust/injections.scm`. Note that Zed may need to be restarted for these changes to take effect.

### Syntax highlighting

In Tree-sitter, the `highlights.scm` file defines syntax highlighting rules for a particular syntax.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ For example, add the following to your `settings.json` if you wish to override t
}
```

To see a comprehensive list of list of captures (like `comment` and `comment.doc`) see [Language Extensions: Syntax highlighting](./extensions/languages.md#syntax-highlighting).
To see a comprehensive list of list of captures (like `comment` and `comment.doc`) see [Language Extensions: Syntax highlighting](./extensions/languages.md#syntax-highlighting). To add new captures, you can use a [custom Tree-sitter query](./extensions/languages.md#tree-sitter-queries).

To see a list of available theme attributes look at the JSON file for your theme.
For example, [assets/themes/one/one.json](https://github.com/zed-industries/zed/blob/main/assets/themes/one/one.json) for the default One Dark and One Light themes.
Expand Down