use crate::config::LanguageConfig;
use crate::primitives::grammar::GrammarEntry;
use crate::primitives::highlight_engine::HighlightEngine;
use crate::primitives::highlighter::Language;
use crate::primitives::GrammarRegistry;
use std::collections::HashMap;
use std::path::Path;
pub struct DetectedLanguage {
pub name: String,
pub display_name: String,
pub highlighter: HighlightEngine,
pub ts_language: Option<Language>,
}
impl DetectedLanguage {
pub fn from_entry(entry: &GrammarEntry, registry: &GrammarRegistry) -> Self {
Self {
name: entry.language_id.clone(),
display_name: entry.display_name.clone(),
highlighter: HighlightEngine::from_entry(entry, registry),
ts_language: entry.engines.tree_sitter,
}
}
pub fn from_path(
path: &Path,
first_line: Option<&str>,
registry: &GrammarRegistry,
languages: &HashMap<String, LanguageConfig>,
) -> Self {
Self::from_path_with_fallback(path, first_line, registry, languages, None)
}
pub fn from_path_with_fallback(
path: &Path,
first_line: Option<&str>,
registry: &GrammarRegistry,
languages: &HashMap<String, LanguageConfig>,
default_language: Option<&str>,
) -> Self {
let config_lang_id = crate::services::lsp::manager::detect_language(path, languages);
let override_name = |mut d: Self| -> Self {
if let Some(id) = config_lang_id.clone() {
d.name = id;
}
d
};
if let Some(entry) = registry.find_by_path(path, first_line) {
return override_name(Self::from_entry(entry, registry));
}
if let Some(lang_key) = default_language {
let grammar = languages
.get(lang_key)
.map(|lc| lc.grammar.as_str())
.filter(|g| !g.is_empty())
.unwrap_or(lang_key);
if let Some(entry) = registry.find_by_name(grammar) {
return override_name(Self::from_entry(entry, registry));
}
}
override_name(Self::plain_text())
}
pub fn from_syntax_name(
name: &str,
registry: &GrammarRegistry,
languages: &HashMap<String, LanguageConfig>,
) -> Option<Self> {
let entry = registry.find_by_name(name)?;
let mut detected = Self::from_entry(entry, registry);
if let Some(id) = resolve_language_id(&entry.display_name, registry, languages) {
detected.name = id;
}
Some(detected)
}
pub fn plain_text() -> Self {
Self {
name: "text".to_string(),
display_name: "Text".to_string(),
highlighter: HighlightEngine::None,
ts_language: None,
}
}
pub fn from_virtual_name(name: &str, registry: &GrammarRegistry) -> Self {
let cleaned = name.trim_matches('*');
let filename = if let Some(pos) = cleaned.rfind(':') {
&cleaned[pos + 1..]
} else {
cleaned
};
registry
.find_by_path(Path::new(filename), None)
.map(|entry| Self::from_entry(entry, registry))
.unwrap_or_else(Self::plain_text)
}
}
pub fn resolve_language_id(
syntax_name: &str,
registry: &GrammarRegistry,
languages: &HashMap<String, LanguageConfig>,
) -> Option<String> {
for (lang_id, lang_config) in languages {
if let Some(entry) = registry.find_by_name(&lang_config.grammar) {
if entry.display_name == syntax_name {
return Some(lang_id.clone());
}
}
}
None
}