Skip to content

Commit 29ab179

Browse files
feat(tree-sitter): support file to language association through globs
* new field (file-globs) in the config allowing to match a language by globs (alternative to file-types) * added tree-sitter ssh-client-config for the ~/.ssh/config file * added the .git/config file to tree-sitter git-config
1 parent 40647f0 commit 29ab179

File tree

6 files changed

+378
-9
lines changed

6 files changed

+378
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/src/generated/lang-support.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
| scala || || `metals` |
7878
| solidity || | | `solc` |
7979
| sql || | | |
80+
| sshclientconfig || | | |
8081
| svelte || || `svelteserver` |
8182
| swift || | | `sourcekit-lsp` |
8283
| tablegen |||| |

helix-core/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std"
4343
etcetera = "0.3"
4444
textwrap = "0.15.0"
4545

46+
globset = "0.4"
47+
4648
[dev-dependencies]
4749
quickcheck = { version = "1", default-features = false }

helix-core/src/syntax.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
};
99

1010
use arc_swap::{ArcSwap, Guard};
11+
use globset::{Glob, GlobSet, GlobSetBuilder};
1112
use slotmap::{DefaultKey as LayerId, HopSlotMap};
1213

1314
use std::{
@@ -25,6 +26,16 @@ use serde::{Deserialize, Serialize};
2526

2627
use helix_loader::grammar::{get_language, load_runtime_file};
2728

29+
fn deserialize_glob_set<'de, D: serde::Deserializer<'de>>(
30+
deserializer: D,
31+
) -> Result<GlobSet, D::Error> {
32+
let globs = Vec::<String>::deserialize(deserializer)?;
33+
let mut builder = GlobSetBuilder::new();
34+
for glob in globs {
35+
builder.add(Glob::new(&glob).map_err(serde::de::Error::custom)?);
36+
}
37+
builder.build().map_err(serde::de::Error::custom)
38+
}
2839
fn deserialize_regex<'de, D>(deserializer: D) -> Result<Option<Regex>, D::Error>
2940
where
3041
D: serde::Deserializer<'de>,
@@ -61,11 +72,14 @@ pub struct Configuration {
6172
pub struct LanguageConfiguration {
6273
#[serde(rename = "name")]
6374
pub language_id: String, // c-sharp, rust
64-
pub scope: String, // source.rust
75+
pub scope: String, // source.rust
76+
#[serde(default)]
6577
pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
78+
#[serde(default, skip_serializing, deserialize_with = "deserialize_glob_set")]
79+
pub file_globs: GlobSet,
6680
#[serde(default)]
6781
pub shebangs: Vec<String>, // interpreter(s) associated with language
68-
pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
82+
pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
6983
pub comment_token: Option<String>,
7084
pub max_line_length: Option<usize>,
7185

@@ -436,6 +450,7 @@ pub struct Loader {
436450
// highlight_names ?
437451
language_configs: Vec<Arc<LanguageConfiguration>>,
438452
language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
453+
language_config_ids_by_globset: Vec<(GlobSet, usize)>,
439454
language_config_ids_by_shebang: HashMap<String, usize>,
440455

441456
scopes: ArcSwap<Vec<String>>,
@@ -447,6 +462,7 @@ impl Loader {
447462
language_configs: Vec::new(),
448463
language_config_ids_by_file_type: HashMap::new(),
449464
language_config_ids_by_shebang: HashMap::new(),
465+
language_config_ids_by_globset: Vec::new(),
450466
scopes: ArcSwap::from_pointee(Vec::new()),
451467
};
452468

@@ -465,6 +481,9 @@ impl Loader {
465481
.language_config_ids_by_shebang
466482
.insert(shebang.clone(), language_id);
467483
}
484+
loader
485+
.language_config_ids_by_globset
486+
.push((config.file_globs.clone(), language_id));
468487

469488
loader.language_configs.push(Arc::new(config));
470489
}
@@ -478,16 +497,27 @@ impl Loader {
478497
let configuration_id = path
479498
.file_name()
480499
.and_then(|n| n.to_str())
481-
.and_then(|file_name| self.language_config_ids_by_file_type.get(file_name))
500+
.and_then(|file_name| {
501+
self.language_config_ids_by_file_type
502+
.get(file_name)
503+
.copied()
504+
})
482505
.or_else(|| {
483506
path.extension()
484507
.and_then(|extension| extension.to_str())
485-
.and_then(|extension| self.language_config_ids_by_file_type.get(extension))
486-
});
487-
488-
configuration_id.and_then(|&id| self.language_configs.get(id).cloned())
508+
.and_then(|extension| {
509+
self.language_config_ids_by_file_type
510+
.get(extension)
511+
.copied()
512+
})
513+
})
514+
.or_else(|| {
515+
self.language_config_ids_by_globset
516+
.iter()
517+
.find_map(|(gs, id)| if gs.is_match(path) { Some(*id) } else { None })
518+
})?;
489519

490-
// TODO: content_regex handling conflict resolution
520+
self.language_configs.get(configuration_id).cloned()
491521
}
492522

493523
pub fn language_config_for_shebang(&self, source: &Rope) -> Option<Arc<LanguageConfiguration>> {

languages.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,8 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-regex", rev = "e1cf
946946
name = "git-config"
947947
scope = "source.gitconfig"
948948
roots = []
949-
# TODO: allow specifying file-types as a regex so we can read directory names (e.g. `.git/config`)
950949
file-types = [".gitmodules", ".gitconfig"]
950+
file-globs = ["*.git/config"]
951951
injection-regex = "git-config"
952952
comment-token = "#"
953953
indent = { tab-width = 4, unit = "\t" }
@@ -1342,3 +1342,14 @@ indent = { tab-width = 4, unit = " " }
13421342
[[grammar]]
13431343
name = "odin"
13441344
source = { git = "https://github.com/MineBill/tree-sitter-odin", rev = "da885f4a387f169b9b69fe0968259ee257a8f69a" }
1345+
1346+
[[language]]
1347+
name = "sshclientconfig"
1348+
scope = "source.sshclientconfig"
1349+
file-globs = ["*.ssh/config"]
1350+
roots = []
1351+
1352+
[[grammar]]
1353+
name = "sshclientconfig"
1354+
source = { git = "https://github.com/metio/tree-sitter-ssh-client-config", rev = "769d7a01a2e5493b4bb5a51096c6bf4be130b024" }
1355+

0 commit comments

Comments
 (0)