use std::sync::OnceLock;
use tree_sitter_highlight::HighlightConfiguration;
use super::queries;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum Lang {
Rust,
Python,
Javascript,
Typescript,
Tsx,
Go,
C,
Cpp,
Java,
CSharp,
Ruby,
Php,
Bash,
Lua,
Json,
Toml,
Yaml,
Html,
Css,
Markdown,
MarkdownInline,
}
pub(super) struct Entry {
pub(super) lang: Lang,
label: &'static str,
exts: &'static [&'static str],
files: &'static [&'static str],
inject: &'static [&'static str],
}
pub(super) const ENTRIES: &[Entry] = &[
Entry {
lang: Lang::Rust,
label: "rust",
exts: &["rs"],
files: &[],
inject: &["rust", "rs"],
},
Entry {
lang: Lang::Python,
label: "python",
exts: &["py", "pyi", "pyw"],
files: &[],
inject: &["python", "py", "python3"],
},
Entry {
lang: Lang::Javascript,
label: "javascript",
exts: &["js", "jsx", "mjs", "cjs"],
files: &[],
inject: &["javascript", "js", "node"],
},
Entry {
lang: Lang::Typescript,
label: "typescript",
exts: &["ts", "mts", "cts"],
files: &[],
inject: &["typescript", "ts"],
},
Entry {
lang: Lang::Tsx,
label: "tsx",
exts: &["tsx"],
files: &[],
inject: &["tsx"],
},
Entry {
lang: Lang::Go,
label: "go",
exts: &["go"],
files: &[],
inject: &["go", "golang"],
},
Entry {
lang: Lang::C,
label: "c",
exts: &["c", "h"],
files: &[],
inject: &["c"],
},
Entry {
lang: Lang::Cpp,
label: "cpp",
exts: &["cpp", "cc", "cp", "cxx", "hpp", "hh", "hxx", "ino", "tpp"],
files: &[],
inject: &["cpp", "c++"],
},
Entry {
lang: Lang::Java,
label: "java",
exts: &["java"],
files: &[],
inject: &["java"],
},
Entry {
lang: Lang::CSharp,
label: "c#",
exts: &["cs", "csx"],
files: &[],
inject: &["csharp", "c#", "cs"],
},
Entry {
lang: Lang::Ruby,
label: "ruby",
exts: &["rb", "rbw", "rake", "gemspec"],
files: &["gemfile", "rakefile", "vagrantfile", "guardfile"],
inject: &["ruby", "rb"],
},
Entry {
lang: Lang::Php,
label: "php",
exts: &["php", "phtml"],
files: &[],
inject: &["php"],
},
Entry {
lang: Lang::Bash,
label: "bash",
exts: &["sh", "bash", "zsh", "ksh"],
files: &[
".bashrc",
".bash_profile",
".bash_aliases",
".bash_logout",
".zshrc",
".zshenv",
".zprofile",
".profile",
"pkgbuild",
"apkbuild",
],
inject: &["bash", "sh", "shell", "zsh", "shell-script"],
},
Entry {
lang: Lang::Lua,
label: "lua",
exts: &["lua"],
files: &[],
inject: &["lua"],
},
Entry {
lang: Lang::Json,
label: "json",
exts: &["json", "jsonc"],
files: &[],
inject: &["json"],
},
Entry {
lang: Lang::Toml,
label: "toml",
exts: &["toml"],
files: &[],
inject: &["toml"],
},
Entry {
lang: Lang::Yaml,
label: "yaml",
exts: &["yaml", "yml"],
files: &[],
inject: &["yaml", "yml"],
},
Entry {
lang: Lang::Html,
label: "html",
exts: &["html", "htm", "xhtml"],
files: &[],
inject: &["html"],
},
Entry {
lang: Lang::Css,
label: "css",
exts: &["css"],
files: &[],
inject: &["css"],
},
Entry {
lang: Lang::Markdown,
label: "markdown",
exts: &["md", "markdown"],
files: &[],
inject: &["markdown", "md"],
},
Entry {
lang: Lang::MarkdownInline,
label: "markdown",
exts: &[],
files: &[],
inject: &["markdown_inline"],
},
];
pub(super) const LANG_COUNT: usize = ENTRIES.len();
pub(super) fn detect(filename: &str) -> Option<Lang> {
let base = filename.rsplit(['/', '\\']).next().unwrap_or(filename);
if let Some(entry) = ENTRIES
.iter()
.find(|e| e.files.iter().any(|name| name.eq_ignore_ascii_case(base)))
{
return Some(entry.lang);
}
let (_, ext) = base.rsplit_once('.').filter(|(stem, _)| !stem.is_empty())?;
ENTRIES
.iter()
.find(|e| e.exts.iter().any(|name| name.eq_ignore_ascii_case(ext)))
.map(|e| e.lang)
}
pub(super) fn label(lang: Lang) -> &'static str {
ENTRIES[lang as usize].label
}
pub(super) fn lang_for_injection(name: &str) -> Option<Lang> {
let name = name.trim();
let name = name
.strip_prefix("source.")
.or_else(|| name.strip_prefix("text."))
.unwrap_or(name);
ENTRIES
.iter()
.find(|e| {
e.inject
.iter()
.any(|alias| alias.eq_ignore_ascii_case(name))
})
.map(|e| e.lang)
}
static CONFIGS: [OnceLock<Option<HighlightConfiguration>>; LANG_COUNT] =
[const { OnceLock::new() }; LANG_COUNT];
pub(super) fn config(lang: Lang) -> Option<&'static HighlightConfiguration> {
CONFIGS[lang as usize]
.get_or_init(|| queries::compile(lang).ok())
.as_ref()
}