打开一个项目, 这个项目里有各种各样的文件, 那么首先要做的就是区分每一个文件属于什么语言. 一方面一个语言可能对应很多种文件, 另一方面一个文件后缀也不能直接用于判定语言. 那么我们要给一个新语言注册哪些信息呢?
唯一标识符, 禁止冲突
给人看的识别符号, 用于多语言国际化, 所有加 display 的默认是本地化的 id.
最高优先级, jetbrain 里是这玩意儿 ![]() vscode 里用 files.associations 标记(Workspace 级)
高优先级, 查多媒体类型, 属于比较标准化的方法, 网络环境也适用.
高优先级, 宣称某个名称该语言所有, 很多为
低优先级, 宣称某种后缀归该语言所有.
后缀名搞到现在, 确实挺复杂的, 小写 注意只是 windows 不区分大小写, 不是说 ntfs 读不出大小写. VSCode 不区分这两者, 且宣称这是 feature 而不是 bug.
亲代, 语言怎么还有亲代呢? 语言当然有亲代, 比如 json 之于 json5, js 之于 jsx, xml 之于 html. 语言可以从亲代那里继承各种定义, 比如颜色定义, 节点类型之类的. 注意 LanguageInstance 是惰性加载的, 子代找定义, 有可能亲代没加载, 那么子代可以通知亲代初始化. 那核心结构体就这样 #[derive(Clone, Debug)]pub struct LanguageInstance { pub id: LanguageID, pub debug_name: &'static str, pub display_name: String, pub parent: Option<LanguageID>, pub case_insensitive: bool, pub file_names: Vec<String>, pub file_extensions: Vec<String>, pub file_mimes: Vec<Mime>, } pub struct LanguageFileResolver { user_edit: DashMap<String, LanguageID>, } 注意, 用户能自己添加删除 file_pattern, 所以你得记录用户的修改事件, 然后根据插件加载的语言, 合成真正的 resolver. 另外我们的文件可能在远端, 所以我们使用 url 来标记文件路径. 还有一种情况是 url 是 data 编码的, 此时需要判定 MIME impl LanguageFileResolver {// normal url pub fn check_file_name(&self, languages: &LanguageRegistry, file_url: &Url) -> PsiResult<LanguageID> { // last part of url let file_name = match file_url.path_segments().and_then(|mut s| s.last()) { Some(s) => s, None => Err(PsiError::runtime_error(format!("File name of `{}` not valid", file_url)))?, }; for language in languages.get_all_languages() { for support_name in language.file_names.iter() { if language.case_insensitive { if file_name.eq_ignore_ascii_case(support_name) { return Ok(language.id); } } else { if file_name.eq(support_name) { return Ok(language.id); } } } } for get_all_language in languages.get_all_languages() { for file_extension in get_all_language.file_extensions.iter() { if file_name.ends_with(file_extension) { return Ok(get_all_language.id); } } } Err(PsiError::runtime_error(format!("File `{}` does not match any language registered", file_url))) } // "data:text/plain,HelloWorld" pub fn check_file_mime(&self, languages: &LanguageRegistry, mime: &Mime) -> PsiResult<LanguageID> { for language in languages.get_all_languages() { for file_mime in language.file_mimes.iter() { if file_mime == mime { return Ok(language.id); } } } Err(PsiError::runtime_error(format!("Mime type `{}` does not match any language registered", mime))) } } 代码同步于 |
万奢网手机版
官网微博:万奢网服务平台