diff --git a/CHANGELOG.md b/CHANGELOG.md index 37f435d3..e1f26ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Features +- Added support for `hidden_file_extensions` from `.sublime-syntax` files, see #3613 (@Matei02355) - Add word wrapping mode via `--wrap=word`, see #3597 (@veeceey) - Implement `--unbuffered` mode for streaming input, allowing partial lines to display immediately (e.g. `tail -f | bat -u`). Closes #3555, see #3583 (@mainnebula) - Added an initial `flake.nix` for a ready made development environment; see #3578 (@vorburger) diff --git a/src/assets.rs b/src/assets.rs index 82c160c9..80ae3e57 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -262,6 +262,24 @@ impl HighlightingAssets { .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })) } + fn find_syntax_by_hidden_file_name( + &self, + file_name: &OsStr, + ) -> Result>> { + let Some(hidden_file_extension) = file_name + .to_str() + .and_then(|name| name.strip_prefix('.')) + .filter(|name| !name.is_empty()) + else { + return Ok(None); + }; + + // syntect stores `hidden_file_extensions` in the same extension list as + // regular file extensions, but dotfiles must be queried without the + // leading period. + self.find_syntax_by_extension(Some(OsStr::new(hidden_file_extension))) + } + fn find_syntax_by_token(&self, token: &str) -> Result>> { let syntax_set = self.get_syntax_set()?; Ok(syntax_set @@ -275,6 +293,9 @@ impl HighlightingAssets { ignored_suffixes: &IgnoredSuffixes, ) -> Result>> { let mut syntax = self.find_syntax_by_extension(Some(file_name))?; + if syntax.is_none() { + syntax = self.find_syntax_by_hidden_file_name(file_name)?; + } if syntax.is_none() { syntax = ignored_suffixes.try_with_stripped_suffix(file_name, |stripped_file_name| { @@ -661,6 +682,55 @@ mod tests { ); } + #[cfg(feature = "build-assets")] + #[test] + fn syntax_detection_hidden_file_extensions() { + let source_dir = TempDir::new().expect("creation of temporary source directory"); + let cache_dir = TempDir::new().expect("creation of temporary cache directory"); + let syntax_dir = source_dir.path().join("syntaxes"); + + std::fs::create_dir_all(&syntax_dir).expect("creation of syntax directory succeeds"); + std::fs::write( + syntax_dir.join("HiddenFileExtension.sublime-syntax"), + r#"%YAML 1.2 +--- +name: Hidden File Extension +hidden_file_extensions: + - testrc +scope: source.hiddenfileextension + +contexts: + main: + - match: . + scope: source.hiddenfileextension +"#, + ) + .expect("custom syntax can be written"); + + build( + source_dir.path(), + false, + false, + cache_dir.path(), + env!("CARGO_PKG_VERSION"), + ) + .expect("custom assets can be built"); + + let test = SyntaxDetectionTest { + assets: HighlightingAssets::from_cache(cache_dir.path()) + .expect("custom syntax cache can be loaded"), + syntax_mapping: SyntaxMapping::new(), + temp_dir: TempDir::new().expect("creation of temporary directory"), + }; + + assert_eq!(test.syntax_for_file(".testrc"), "Hidden File Extension"); + assert_eq!( + test.syntax_for_stdin_with_content(".testrc", b""), + "Hidden File Extension" + ); + assert!(test.syntax_is_same_for_inputkinds(".testrc", "")); + } + #[cfg(unix)] #[test] fn syntax_detection_for_symlinked_file() {