grammars: Highlight class instantiations in JavaScript, TypeScript, and TSX (#61890)

## Why

JavaScript, TypeScript, and TSX class declarations were captured as
`type.class`, but the same class names in `new` expressions were
captured as the generic `type`. Themes that define a distinct style for
`type.class` therefore rendered declarations and instantiations
inconsistently.

## What

Update the three highlight queries so constructor identifiers in `new`
expressions use `type.class`. Themes without a dedicated `type.class`
style continue to fall back to their existing `type` style.

Add a regression test that loads the real JavaScript, TypeScript, and
TSX grammars with distinct colors for `type` and `type.class`, then
verifies that both the declaration and instantiation receive the
class-specific highlight.

## Testing

- `cargo test -p languages test_class_instantiation_highlighting`
- `cargo test -p languages`
- `cargo fmt --all -- --check`
- `./script/clippy -p languages`

## References

Closes #55943

Release Notes:

- Fixed inconsistent class instantiation highlighting in JavaScript,
TypeScript, and TSX.
This commit is contained in:
Ibrahim Khan authored and GitHub committed 2026-07-30 21:16:10 +00:00
1 parent c8ab9c91a0
commit e476383ca8
4 files changed
+34 -4

No files matched your search

@@ -30,7 +30,7 @@
] @function.method))
(new_expression
constructor: (identifier) @type)
constructor: (identifier) @type.class)
(nested_type_identifier
module: (identifier) @type)
+1 -1
View File
@@ -30,7 +30,7 @@
] @function.method))
(new_expression
constructor: (identifier) @type)
constructor: (identifier) @type.class)
(nested_type_identifier
module: (identifier) @type)
@@ -123,7 +123,7 @@
] @function.method))
(new_expression
constructor: (identifier) @type)
constructor: (identifier) @type.class)
(nested_type_identifier
module: (identifier) @type)
+31 -1
View File
@@ -908,10 +908,12 @@ async fn get_cached_ts_server_binary(
mod tests {
use std::path::Path;
use gpui::{AppContext as _, BackgroundExecutor, TestAppContext};
use gpui::{AppContext as _, BackgroundExecutor, Hsla, TestAppContext};
use project::FakeFs;
use rope::Rope;
use serde_json::json;
use task::TaskTemplates;
use theme::SyntaxTheme;
use unindent::Unindent;
use util::{path, rel_path::rel_path};
@@ -919,6 +921,34 @@ mod tests {
PackageJsonData, TypeScriptContextProvider, replace_test_name_parameters,
};
#[test]
fn test_class_instantiation_highlighting() {
let source = Rope::from("class Dog {}\nconst dog = new Dog();");
let theme = SyntaxTheme::new_test([("type", Hsla::blue()), ("type.class", Hsla::green())]);
for language in [
crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
crate::language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into()),
crate::language("javascript", tree_sitter_typescript::LANGUAGE_TSX.into()),
] {
language.set_theme(&theme);
let class_highlight = language
.grammar()
.and_then(|grammar| grammar.highlight_id_for_name("type.class"))
.expect("type.class highlight should be defined");
assert_eq!(
language.highlight_text(&source, 0..source.len()),
vec![(6..9, class_highlight), (29..32, class_highlight)],
"{} class instantiations should use the type.class highlight",
language.name()
);
}
}
#[gpui::test]
async fn test_outline(cx: &mut TestAppContext) {
for language in [