| | @@ -121,6 +121,17 @@ fn syntaxes() -> &'static SyntaxSet { |
| 121 | 121 | SYNTAXES.get_or_init(two_face::syntax::extra_newlines) |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | +/// Extensions no grammar in the set claims, and the grammar to read them with anyway. |
| 125 | +/// |
| 126 | +/// `two-face`'s JavaScript grammar registers `js` and `htc` and nothing else, so `.jsx` |
| 127 | +/// rendered as plain text while `.tsx` was highlighted, TypeScriptReact being its own |
| 128 | +/// grammar. JSX is JavaScript with element literals in it and the JavaScript grammar |
| 129 | +/// reads it well enough. |
| 130 | +/// |
| 131 | +/// One entry, deliberately: every alias is a claim that two languages are close enough |
| 132 | +/// to read as one, and a wrong one is worse than no colour. |
| 133 | +const ALIASES: &[(&str, &str)] = &[("jsx", "js")]; |
| 134 | + |
| 124 | 135 | /// The language, from the file name and then the first line. |
| 125 | 136 | /// |
| 126 | 137 | /// This is `SyntaxSet::find_syntax_for_file` without the filesystem: that method opens |
| | @@ -135,9 +146,19 @@ fn syntax_for<'a>( |
| 135 | 146 | ) -> Option<&'a SyntaxReference> { |
| 136 | 147 | let extension = file_name.rsplit_once('.').map(|(_, ext)| ext); |
| 137 | 148 | |
| 149 | + // After the set's own answer, never before it: an alias is what to do when no |
| 150 | + // grammar claims the extension, not a way to overrule one that does. |
| 151 | + let aliased = extension.and_then(|ext| { |
| 152 | + ALIASES |
| 153 | + .iter() |
| 154 | + .find(|(from, _)| *from == ext) |
| 155 | + .map(|(_, to)| *to) |
| 156 | + }); |
| 157 | + |
| 138 | 158 | let syntax = syntaxes |
| 139 | 159 | .find_syntax_by_extension(file_name) |
| 140 | 160 | .or_else(|| extension.and_then(|ext| syntaxes.find_syntax_by_extension(ext))) |
| 161 | + .or_else(|| aliased.and_then(|to| syntaxes.find_syntax_by_extension(to))) |
| 141 | 162 | .or_else(|| syntaxes.find_syntax_by_first_line(text.lines().next().unwrap_or("")))?; |
| 142 | 163 | |
| 143 | 164 | (syntax.name != syntaxes.find_syntax_plain_text().name).then_some(syntax) |
| | @@ -310,7 +331,8 @@ mod tests { |
| 310 | 331 | |
| 311 | 332 | /// The gap `two_face` was added to close, asserted rather than described: TOML, |
| 312 | 333 | /// Dockerfile and TypeScript rendered plain until it landed, and the first two are |
| 313 | | − /// in this repository. `.jsx` is still absent — see the handover. |
| 334 | + /// in this repository. `.jsx` is here through [`ALIASES`] rather than through a |
| 335 | + /// grammar of its own. |
| 314 | 336 | #[test] |
| 315 | 337 | fn the_languages_this_repository_uses_are_all_highlighted() { |
| 316 | 338 | for name in [ |
| | @@ -327,6 +349,7 @@ mod tests { |
| 327 | 349 | "Makefile", |
| 328 | 350 | "app.py", |
| 329 | 351 | "index.js", |
| 352 | + "App.jsx", |
| 330 | 353 | "main.go", |
| 331 | 354 | "query.sql", |
| 332 | 355 | "main.c", |