Skip to content

fix(typescript) highlight generic function declarations - #4551

Open
Kernel-Hunter wants to merge 1 commit into
highlightjs:mainfrom
Kernel-Hunter:fix-ts-generic-function-title
Open

Kernel-Hunter wants to merge 1 commit into
highlightjs:mainfrom
Kernel-Hunter:fix-ts-generic-function-title

Conversation

@Kernel-Hunter

Copy link
Copy Markdown

Fixes #4550

Changes

The function declaration rule comes from the JavaScript grammar and only matches when ( directly follows the name. With type parameters in between, as in function identity<T>(arg: T), nothing matched: the name got no title.function and the arguments weren't scoped as params.

This adds a TypeScript-only variant for function name<, and a mode for the type parameter list that handles nested <...> (T extends Record<string, unknown>) and ignores the > of => inside function types (F extends (x: number) => void). After the list closes, the existing params rule picks up the argument list. Output for non-generic functions and plain JS is unchanged.

Not covered: function* gen<T>() and anonymous function<T>(). I can do those separately if you want them.

Checklist

  • Added markup tests (test/markup/typescript/generic-functions)
  • Updated the changelog at CHANGES.md
  • I have read and followed our AI-assisted contributions policy

Assisted-by: Claude Opus 5.5

The function declaration rule comes from the JavaScript grammar and
expects `(` right after the name. With type parameters in between
(`function identity<T>(arg: T)`) it never matched, so the name got no
title.function scope and the parameters were not scoped as params.

Add a TypeScript-only variant that matches when the name is followed
by `<`, plus a type parameter mode that handles nested `<...>` and
skips the `>` of `=>` inside function types. The parameter list after
it is picked up by the existing params rule.

Assisted-by: Claude Opus 5.5

@kgeminicdev kgeminicdev left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this fixes #4550 nicely. Nested generics, defaults, => in function types and declare function all highlight correctly, and plain functions are unchanged. One regression with string literal types in the type parameters, details and a tested fix inline.

// `=>` inside a function type must not close the list
{ match: /=>/ },
tsLanguage.exports.CLASS_REFERENCE,
'self'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A > inside a string literal type closes the type parameter list early, since strings aren't in contains here. On main these highlight correctly; with this change:

  • function f<T extends "a>b">(x: T) {}: the string is cut at >, and ">(x: T) {} is highlighted as a string.
  • function f<T extends `${number}>`>(x: T) {}: the template literal is cut, and since template strings span lines, everything after it is highlighted as a string.

Adding the string modes fixed both cases for me, and generic-functions still produces the same output:

contains: [
  { match: /=>/ },
  tsLanguage.exports.CLASS_REFERENCE,
  hljs.APOS_STRING_MODE,
  hljs.QUOTE_STRING_MODE,
  tsLanguage.contains.find(m => m.begin === '`'), // or export TEMPLATE_STRING alongside CLASS_REFERENCE
  'self'
]

Might be worth adding both lines to the markup test.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(typescript) generic function declarations lose name and params highlighting

3 participants