diff --git a/cli/loader/src/lib.rs b/cli/loader/src/lib.rs index c61bd1c6..b3ef2378 100644 --- a/cli/loader/src/lib.rs +++ b/cli/loader/src/lib.rs @@ -522,11 +522,15 @@ impl Loader { .and_then(|n| n.to_str()) .and_then(|file_name| self.language_configuration_ids_by_file_type.get(file_name)) .or_else(|| { - path.extension() - .and_then(|extension| extension.to_str()) - .and_then(|extension| { - self.language_configuration_ids_by_file_type.get(extension) - }) + let mut path = path.to_owned(); + let mut extensions = Vec::with_capacity(2); + while let Some(extension) = path.extension() { + extensions.push(extension.to_str()?.to_string()); + path = PathBuf::from(path.file_stem()?.to_os_string()); + } + extensions.reverse(); + self.language_configuration_ids_by_file_type + .get(&extensions.join(".")) }); if let Some(configuration_ids) = configuration_ids { diff --git a/cli/src/tests/detect_language.rs b/cli/src/tests/detect_language.rs index eeb24855..50bb891e 100644 --- a/cli/src/tests/detect_language.rs +++ b/cli/src/tests/detect_language.rs @@ -89,6 +89,120 @@ fn detect_language_by_first_line_regex() { ); } +#[test] +fn detect_langauge_by_double_barrel_file_extension() { + let blade_dir = tree_sitter_dir( + r#"{ + "grammars": [ + { + "name": "blade", + "path": ".", + "scope": "source.blade", + "file-types": [ + "blade.php" + ] + } + ], + "metadata": { + "version": "0.0.1" + } +} +"#, + "blade", + ); + + let mut loader = Loader::with_parser_lib_path(scratch_dir().to_path_buf()); + let config = loader + .find_language_configurations_at_path(blade_dir.path(), false) + .unwrap(); + + // this is just to validate that we can read the tree-sitter.json correctly + assert_eq!(config[0].scope.as_ref().unwrap(), "source.blade"); + + let file_name = blade_dir.path().join("foo.blade.php"); + fs::write(&file_name, "").unwrap(); + assert_eq!( + get_lang_scope(&loader, &file_name), + Some("source.blade".into()) + ); +} + +#[test] +fn detect_language_without_filename() { + let gitignore_dir = tree_sitter_dir( + r#"{ + "grammars": [ + { + "name": "gitignore", + "path": ".", + "scope": "source.gitignore", + "file-types": [ + ".gitignore" + ] + } + ], + "metadata": { + "version": "0.0.1" + } +} +"#, + "gitignore", + ); + + let mut loader = Loader::with_parser_lib_path(scratch_dir().to_path_buf()); + let config = loader + .find_language_configurations_at_path(gitignore_dir.path(), false) + .unwrap(); + + // this is just to validate that we can read the tree-sitter.json correctly + assert_eq!(config[0].scope.as_ref().unwrap(), "source.gitignore"); + + let file_name = gitignore_dir.path().join(".gitignore"); + fs::write(&file_name, "").unwrap(); + assert_eq!( + get_lang_scope(&loader, &file_name), + Some("source.gitignore".into()) + ); +} + +#[test] +fn detect_language_without_file_extension() { + let ssh_config_dir = tree_sitter_dir( + r#"{ + "grammars": [ + { + "name": "ssh_config", + "path": ".", + "scope": "source.ssh_config", + "file-types": [ + "ssh_config" + ] + } + ], + "metadata": { + "version": "0.0.1" + } +} +"#, + "ssh_config", + ); + + let mut loader = Loader::with_parser_lib_path(scratch_dir().to_path_buf()); + let config = loader + .find_language_configurations_at_path(ssh_config_dir.path(), false) + .unwrap(); + + // this is just to validate that we can read the tree-sitter.json correctly + assert_eq!(config[0].scope.as_ref().unwrap(), "source.ssh_config"); + + let file_name = ssh_config_dir.path().join("ssh_config"); + fs::write(&file_name, "").unwrap(); + assert_eq!( + get_lang_scope(&loader, &file_name), + Some("source.ssh_config".into()) + ); +} + fn tree_sitter_dir(tree_sitter_json: &str, name: &str) -> tempfile::TempDir { let temp_dir = tempfile::tempdir().unwrap(); fs::write(temp_dir.path().join("tree-sitter.json"), tree_sitter_json).unwrap();