diff --git a/contrib/internal/treesitter/update-language-info.py b/contrib/internal/treesitter/update-language-info.py
new file mode 100755
index 0000000000000000000000000000000000000000..cede9178bfd30593ef31e04ff1f6ab3c510e38ad

--- /dev/null
+++ b/contrib/internal/treesitter/update-language-info.py
@@ -0,0 +1,661 @@
+#!/usr/bin/env python3
+"""Update the TreeSitter language information.
+
+Version Added:
+    8.0
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import logging
+import sys
+from collections import defaultdict
+from dataclasses import dataclass
+from functools import reduce
+from inspect import cleandoc
+from pathlib import Path
+from typing import TYPE_CHECKING, get_args
+
+from tree_sitter_language_pack import SupportedLanguage
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator, Mapping, Sequence
+
+    from typelets.json import JSONDict
+
+
+logger = logging.getLogger(__name__)
+
+
+#: MIME types for each supported language.
+#:
+#: Version Added:
+#:     8.0
+LANGUAGE_MIME_TYPES: Mapping[str, list[str]] = {
+    'actionscript': [],
+    'ada': ['text/x-ada'],
+    'agda': [],
+    'apex': ['text/x-apex'],
+    'arduino': [],
+    'asm': [
+        'text/x-assembly',
+        'text/x-asm',
+        'text/x-nasm',
+    ],
+    'astro': [],
+    'bash': [
+        'text/x-shell',
+        'text/x-shellscript',
+        'application/x-shellscript',
+        'application/x-sh',
+        'text/x-sh',
+        'text/x-bash',
+    ],
+    'beancount': [],
+    'bibtex': [
+        'application/x-bibtex',
+        'text/x-bibtex',
+    ],
+    'bicep': [],
+    'bitbake': ['text/x-bitbake'],
+    'c': [
+        'text/x-c',
+        'text/x-csrc',
+        'text/x-chdr',
+    ],
+    'cairo': [],
+    'capnp': [],
+    'chatito': [],
+    'clarity': [],
+    'clojure': [
+        'text/x-clojure',
+    ],
+    'cmake': ['text/x-cmake'],
+    'comment': [],
+    'commonlisp': ['text/x-commonlisp'],
+    'cpon': [],
+    'cpp': [
+        'text/x-c++',
+        'text/x-cpp',
+        'text/x-c++src',
+        'text/x-cxx',
+        'text/x-c++hdr',
+    ],
+    'csharp': [
+        'text/x-csharp',
+        'text/x-cs',
+    ],
+    'css': ['text/css'],
+    'csv': [
+        'text/csv',
+        'application/csv',
+    ],
+    'cuda': [
+        'text/x-cuda',
+    ],
+    'd': [
+        'text/x-dlang',
+    ],
+    'dart': [
+        'text/x-dart',
+    ],
+    'dockerfile': [
+        'text/x-dockerfile',
+        'application/x-dockerfile',
+    ],
+    'doxygen': [],
+    'dtd': ['application/xml-dtd'],
+    'elisp': ['text/x-elisp'],
+    'elixir': ['text/x-elixir'],
+    'elm': ['text/x-elm'],
+    'embeddedtemplate': [],
+    'erlang': ['text/x-erlang'],
+    'fennel': [],
+    'firrtl': [],
+    'fish': ['text/x-fish'],
+    'fortran': ['text/x-fortran'],
+    'func': [],
+    'gdscript': ['text/x-gdscript'],
+    'gitattributes': [],
+    'gitcommit': [],
+    'gitignore': [],
+    'gleam': [],
+    'glsl': ['text/x-glsl'],
+    'gn': [],
+    'go': [
+        'text/x-go',
+        'text/x-golang',
+    ],
+    'gomod': [],
+    'gosum': [],
+    'graphql': [],
+    'groovy': [],
+    'gstlaunch': [],
+    'hack': [
+        'text/x-hack',
+        'application/x-hack',
+    ],
+    'hare': [],
+    'haskell': ['text/x-haskell'],
+    'haxe': [],
+    'hcl': ['text/x-hcl'],
+    'hlsl': ['text/x-hlsl'],
+    'html': [
+        'application/html',
+        'application/xhtml',
+        'application/xhtml+xml',
+        'text/html',
+        'text/xtml',
+    ],
+    'hyprlang': [],
+    'ini': [],
+    'ispc': ['text/x-ispc'],
+    'janet': [],
+    'java': [
+        'text/x-java',
+        'text/x-java-source',
+    ],
+    'javascript': [
+        'text/javascript',
+        'application/javascript',
+        'application/x-javascript',
+        'application/ecmascript',
+        'text/ecmascript',
+        'text/jsx',
+    ],
+    'jsdoc': [],
+    'json': [
+        'application/json',
+        'text/json',
+        'application/json5',
+    ],
+    'jsonnet': [],
+    'julia': ['text/x-julia'],
+    'kconfig': [],
+    'kdl': [],
+    'kotlin': ['text/x-kotlin'],
+    'latex': [
+        'text/x-latex',
+        'application/x-latex',
+        'text/x-tex',
+        'application/x-tex',
+    ],
+    'linkerscript': [],
+    'llvm': [
+        'text/x-llvm',
+        'application/x-llvm',
+    ],
+    'lua': [
+        'text/x-lua',
+        'application/x-lua',
+    ],
+    'luadoc': [],
+    'luap': [],
+    'luau': ['text/x-luau'],
+    'magik': ['text/x-magik'],
+    'make': ['text/x-makefile'],
+    'markdown': [
+        'text/markdown',
+        'text/x-markdown',
+        'application/x-gfm',
+    ],
+    'markdown_inline': [],
+    'matlab': [
+        'text/x-matlab',
+        'text/x-octave',
+    ],
+    'mermaid': [],
+    'meson': ['text/x-meson'],
+    'netlinx': [],
+    'nim': [],
+    'ninja': ['text/x-ninja'],
+    'nix': [
+        'application/x-nix',
+        'text/x-nix',
+    ],
+    'objc': [
+        'text/x-objective-c',
+        'text/x-objcsrc',
+        'text/x-objchdr',
+    ],
+    'ocaml': [
+        'text/x-ocaml',
+    ],
+    'ocaml_interface': [],
+    'odin': [],
+    'org': [],
+    'pascal': ['text/x-pascal'],
+    'pem': [],
+    'perl': [
+        'text/x-perl',
+        'application/x-perl',
+    ],
+    'pgn': [],
+    'php': [
+        'text/x-php',
+        'application/x-httpd-php',
+        'application/x-httpd-php-source',
+    ],
+    'po': ['text/x-po'],
+    'pony': ['text/x-pony'],
+    'powershell': [
+        'text/x-powershell',
+        'application/x-powershell',
+    ],
+    'printf': [],
+    'prisma': ['text/x-prisma'],
+    'properties': ['text/x-properties'],
+    'proto': [
+        'text/x-proto',
+        'application/x-protobuf',
+    ],
+    'psv': ['text/x-psv'],
+    'puppet': ['text/x-puppet'],
+    'purescript': ['text/x-purescript'],
+    'pymanifest': [],
+    'python': [
+        'text/x-python',
+        'text/x-python3',
+        'application/x-python',
+        'application/x-python-code',
+    ],
+    'qmldir': [],
+    'qmljs': [],
+    'query': [],
+    'r': [
+        'text/x-r',
+        'application/x-r',
+    ],
+    'racket': ['text/x-racket'],
+    're2c': [],
+    'readline': [],
+    'rego': [],
+    'requirements': [],
+    'ron': [],
+    'rst': [
+        'text/x-rst',
+        'text/x-restructuredtext',
+    ],
+    'ruby': [
+        'text/x-ruby',
+        'application/x-ruby',
+    ],
+    'rust': [
+        'text/x-rust',
+        'text/rust',
+    ],
+    'scala': ['text/x-scala'],
+    'scheme': ['text/x-scheme'],
+    'scss': ['text/scss'],
+    'smali': [],
+    'smithy': [],
+    'solidity': ['text/x-solidity'],
+    'sparql': [
+        'application/x-sparql-query',
+        'text/x-sparql',
+    ],
+    'sql': [
+        'text/x-sql',
+        'application/sql',
+        'application/x-sql',
+        'text/x-plsql',
+    ],
+    'squirrel': [],
+    'starlark': [],
+    'svelte': [],
+    'swift': ['text/x-swift'],
+    'tablegen': [],
+    'tcl': [
+        'text/x-tcl',
+        'application/x-tcl',
+    ],
+    'terraform': [
+        'text/x-terraform',
+        'application/x-terraform',
+    ],
+    'test': [],
+    'thrift': [
+        'text/x-thrift',
+        'application/x-thrift',
+    ],
+    'toml': [
+        'text/x-toml',
+        'application/toml',
+    ],
+    'tsv': ['text/tab-separated-values'],
+    'tsx': [
+        'text/tsx',
+        'text/x-tsx',
+    ],
+    'typescript': [
+        'text/typescript',
+        'application/typescript',
+        'text/x-typescript',
+    ],
+    'typst': [],
+    'udev': [],
+    'ungrammar': [],
+    'uxntal': [],
+    'v': [],
+    'verilog': [],
+    'vhdl': [],
+    'vim': [],
+    'vue': [],
+    'wgsl': ['text/x-wgsl'],
+    'xcompose': [],
+    'xml': [
+        'text/xml',
+        'application/xml',
+        'application/rss+xml',
+        'application/atom+xml',
+        'image/svg+xml',
+    ],
+    'yaml': [
+        'text/x-yaml',
+        'application/yaml',
+        'application/x-yaml',
+        'text/yaml',
+        'text/vnd.yaml',
+    ],
+    'yuck': [],
+    'zig': ['text/x-zig'],
+}
+
+
+#: reviewboard.treesitter module directory.
+#: Version Added:
+#:     8.0
+module_dir = Path(__file__).parents[3] / 'reviewboard' / 'treesitter'
+
+
+@dataclass
+class GrammarInfo:
+    """Information about a grammar.
+
+    Version Added:
+        8.0
+    """
+
+    #: A list of filename suffixes that this language applies to.
+    file_suffixes: list[str]
+
+    #: A regular expression to check against the first line of the file.
+    first_line_regex: str | None
+
+    #: The grammar name.
+    name: str
+
+
+def load_grammar_info(
+    base_path: Path,
+    data: JSONDict,
+) -> GrammarInfo:
+    """Load grammar info from JSON data.
+
+    Version Added:
+        8.0
+
+    Args:
+        base_path (pathlib.Path):
+            The path to the grammar checkout.
+
+        data (typelets.json.JSONDict):
+            The loaded grammar data.
+
+    Returns:
+        GrammarInfo:
+        The loaded grammar info.
+    """
+    file_suffixes = data.get('file-types')
+
+    if file_suffixes is None:
+        file_suffixes = []
+
+    if not isinstance(file_suffixes, list):
+        logger.warning(
+            'Invalid data for key "file-types" in file suffixes %r from %s',
+            file_suffixes, base_path)
+        file_suffixes = []
+
+    first_line_regex = data.get('first-line-regex')
+
+    if first_line_regex is not None and not isinstance(first_line_regex, str):
+        logger.warning(
+            'Invalid data "%s" for key "first-line-regex" in data from %s',
+            first_line_regex, base_path)
+        first_line_regex = None
+
+    return GrammarInfo(
+        name=base_path.stem,
+        file_suffixes=file_suffixes,
+        first_line_regex=first_line_regex)
+
+
+def get_grammars(
+    path: Path,
+) -> Iterator[GrammarInfo]:
+    """Yield the grammar blobs for a package.
+
+    Version Added:
+        8.0
+
+    Args:
+        path (pathlib.Path):
+            The path to the grammar.
+
+    Yields:
+        typelets.json.JSONDict:
+        The loaded info about the grammar.
+    """
+    try:
+        with (path / 'tree-sitter.json').open() as f:
+            p = json.load(f)
+
+            for grammar in p['grammars']:
+                yield load_grammar_info(path, grammar)
+    except OSError:
+        # No tree-sitter.json file present.
+        pass
+
+    try:
+        with (path / 'package.json').open() as f:
+            p = json.load(f)
+
+            if 'tree-sitter' in p:
+                for grammar in p['tree-sitter']:
+                    yield load_grammar_info(path, grammar)
+    except OSError:
+        # No package.json file present.
+        pass
+
+
+def load_grammars(
+    vendor_path: Path,
+    languages: set[str],
+) -> dict[str, GrammarInfo]:
+    """Load the grammar information.
+
+    Version Added:
+        8.0
+
+    Args:
+        vendor_path (pathlib.Path):
+            The path to the vendor directory.
+
+        languages (set of str):
+            The set of languages to limit to (for debugging purposes). If this
+            is empty, all languages in the vendor directory will be loaded.
+
+    Returns:
+        dict:
+        A mapping from grammar name to grammar information.
+    """
+    # tree-sitter-language-pack directly depends on these Python packages, and
+    # therefore those don't get cloned into vendor. We therefore hardcode the
+    # data for these:
+    #   * tree-sitter-c-sharp
+    #   * tree-sitter-embedded-template
+    #   * tree-sitter-yaml
+    result: dict[str, list[GrammarInfo]] = defaultdict(list, {
+        'csharp': [
+            GrammarInfo(file_suffixes=['cs'], first_line_regex=None,
+                        name='csharp'),
+        ],
+        'embeddedtemplate': [
+            GrammarInfo(file_suffixes=['ejs', 'erb'], first_line_regex=None,
+                        name='embeddedtemplate'),
+        ],
+        'yaml': [
+            GrammarInfo(file_suffixes=['yml', 'yaml'], first_line_regex=None,
+                        name='yaml'),
+        ],
+    })
+
+    for language in languages:
+        subdir = vendor_path / language
+
+        for grammar in get_grammars(subdir):
+            result[grammar.name].append(grammar)
+
+    def merge_info(
+        a: GrammarInfo,
+        b: GrammarInfo,
+    ) -> GrammarInfo:
+        if a.name != b.name:
+            raise ValueError(
+                f'Error while merging GrammarInfo: names {a.name} and '
+                f'{b.name} do not match!')
+
+        return GrammarInfo(
+            file_suffixes=list(
+                set(a.file_suffixes) | set(b.file_suffixes)
+            ),
+            first_line_regex=a.first_line_regex or b.first_line_regex,
+            name=a.name,
+        )
+
+    return {
+        name: reduce(merge_info, infos)
+        for name, infos in result.items()
+    }
+
+
+def parse_arguments(
+    args: Sequence[str],
+) -> argparse.Namespace:
+    """Parse the command-line arguments and return the results.
+
+    Version Added:
+        8.0
+
+    Args:
+        args (list of bytes):
+            The arguments to parse.
+    """
+    parser = argparse.ArgumentParser(
+        'Update the TreeSitter language information.',
+        usage='%(prog)s [options]',
+    )
+
+    parser.add_argument('tree_sitter_language_pack_checkout')
+    parser.add_argument('-l', '--language', action='append')
+
+    return parser.parse_args(args)
+
+
+def main() -> None:
+    """Extract data from the tree-sitter vendor packages."""
+    args = parse_arguments(sys.argv[1:])
+
+    tree_sitter_language_pack_path = \
+        Path(args.tree_sitter_language_pack_checkout)
+
+    vendor_path = tree_sitter_language_pack_path / 'vendor'
+
+    if not vendor_path.exists():
+        sys.stderr.write(
+            f'Path {vendor_path} does not exist. Did you run '
+            f'scripts/clone_vendors.py in your tree-sitter-language-pack '
+            f'checkout?'
+        )
+        sys.exit(1)
+
+    languages = args.language or get_args(SupportedLanguage)
+
+    grammars = load_grammars(vendor_path, set(languages))
+
+    with open(module_dir / '_languages.py', 'w', encoding='utf-8') as f:
+        f.write(cleandoc('''
+            """Language information for treesitter.
+
+            Do not make changes directly to this file! This file is auto-
+            generated by contrib/internal/treesitter/update-language-info.py.
+            """
+
+            from __future__ import annotations
+
+            from collections import OrderedDict
+
+            from typing import TYPE_CHECKING
+
+            if TYPE_CHECKING:
+                from collections.abc import Mapping
+
+                from tree_sitter_language_pack import SupportedLanguage
+        '''))
+        f.write('\n\n\n')
+
+        # Generate the MIME_TYPE_TO_LANGUAGE mapping.
+        f.write('MIME_TYPE_TO_LANGUAGE: Mapping[str, SupportedLanguage] = {\n')
+
+        mime_to_lang = {}
+
+        for lang in sorted(grammars.keys()):
+            mime_types = LANGUAGE_MIME_TYPES.get(lang, [])
+
+            for mime_type in mime_types:
+                mime_to_lang[mime_type] = lang
+
+        # Write the mapping sorted by MIME type
+        for mime_type in sorted(mime_to_lang.keys()):
+            lang = mime_to_lang[mime_type]
+            f.write(f'    {mime_type!r}: {lang!r},\n')
+
+        f.write('}\n')
+
+        # Generate the FILE_SUFFIX_TO_LANGUAGES mapping.
+        f.write('\n\n')
+        f.write(
+            'FILE_SUFFIX_TO_LANGUAGES: '
+            'OrderedDict[str, list[SupportedLanguage]] = OrderedDict({\n'
+        )
+
+        suffix_to_langs: dict[str, list[str]] = defaultdict(list)
+
+        for lang in sorted(grammars.keys()):
+            info = grammars[lang]
+            if info.file_suffixes:
+                for suffix in info.file_suffixes:
+                    # If the suffix is very short, we want to enforce it being
+                    # a file extension. Otherwise we can have issues like
+                    # {'t': ['perl']} matching .txt files.
+                    if len(suffix) <= 4 and not suffix.startswith('.'):
+                        suffix = f'.{suffix}'
+
+                    suffix_to_langs[suffix].append(lang)
+
+        # Sort by suffix length (longest first), then alphabetically.
+        sorted_suffixes = sorted(suffix_to_langs.keys(),
+                                 key=lambda suffix: (-len(suffix), suffix))
+
+        for suffix in sorted_suffixes:
+            langs = suffix_to_langs[suffix]
+            f.write(f'    {suffix!r}: {langs!r},\n')
+
+        f.write('})\n')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/reviewboard/treesitter/_languages.py b/reviewboard/treesitter/_languages.py
new file mode 100644
index 0000000000000000000000000000000000000000..537266eb9831d9d911051dedfd694ff6024a93ac

--- /dev/null
+++ b/reviewboard/treesitter/_languages.py
@@ -0,0 +1,433 @@
+"""Language information for treesitter.
+
+Do not make changes directly to this file! This file is auto-
+generated by contrib/internal/treesitter/update-language-info.py.
+"""
+
+from __future__ import annotations
+
+from collections import OrderedDict
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from collections.abc import Mapping
+
+    from tree_sitter_language_pack import SupportedLanguage
+
+
+MIME_TYPE_TO_LANGUAGE: Mapping[str, SupportedLanguage] = {
+    'application/atom+xml': 'xml',
+    'application/csv': 'csv',
+    'application/ecmascript': 'javascript',
+    'application/html': 'html',
+    'application/javascript': 'javascript',
+    'application/json': 'json',
+    'application/json5': 'json',
+    'application/rss+xml': 'xml',
+    'application/sql': 'sql',
+    'application/toml': 'toml',
+    'application/typescript': 'typescript',
+    'application/x-bibtex': 'bibtex',
+    'application/x-dockerfile': 'dockerfile',
+    'application/x-gfm': 'markdown',
+    'application/x-hack': 'hack',
+    'application/x-httpd-php': 'php',
+    'application/x-httpd-php-source': 'php',
+    'application/x-javascript': 'javascript',
+    'application/x-latex': 'latex',
+    'application/x-llvm': 'llvm',
+    'application/x-lua': 'lua',
+    'application/x-nix': 'nix',
+    'application/x-perl': 'perl',
+    'application/x-powershell': 'powershell',
+    'application/x-protobuf': 'proto',
+    'application/x-python': 'python',
+    'application/x-python-code': 'python',
+    'application/x-r': 'r',
+    'application/x-ruby': 'ruby',
+    'application/x-sh': 'bash',
+    'application/x-shellscript': 'bash',
+    'application/x-sparql-query': 'sparql',
+    'application/x-sql': 'sql',
+    'application/x-tcl': 'tcl',
+    'application/x-terraform': 'terraform',
+    'application/x-tex': 'latex',
+    'application/x-thrift': 'thrift',
+    'application/x-yaml': 'yaml',
+    'application/xhtml': 'html',
+    'application/xhtml+xml': 'html',
+    'application/xml': 'xml',
+    'application/xml-dtd': 'dtd',
+    'application/yaml': 'yaml',
+    'image/svg+xml': 'xml',
+    'text/css': 'css',
+    'text/csv': 'csv',
+    'text/ecmascript': 'javascript',
+    'text/html': 'html',
+    'text/javascript': 'javascript',
+    'text/json': 'json',
+    'text/jsx': 'javascript',
+    'text/markdown': 'markdown',
+    'text/rust': 'rust',
+    'text/scss': 'scss',
+    'text/tab-separated-values': 'tsv',
+    'text/tsx': 'tsx',
+    'text/typescript': 'typescript',
+    'text/vnd.yaml': 'yaml',
+    'text/x-ada': 'ada',
+    'text/x-apex': 'apex',
+    'text/x-asm': 'asm',
+    'text/x-assembly': 'asm',
+    'text/x-bash': 'bash',
+    'text/x-bibtex': 'bibtex',
+    'text/x-bitbake': 'bitbake',
+    'text/x-c': 'c',
+    'text/x-c++': 'cpp',
+    'text/x-c++hdr': 'cpp',
+    'text/x-c++src': 'cpp',
+    'text/x-chdr': 'c',
+    'text/x-clojure': 'clojure',
+    'text/x-cmake': 'cmake',
+    'text/x-commonlisp': 'commonlisp',
+    'text/x-cpp': 'cpp',
+    'text/x-cs': 'csharp',
+    'text/x-csharp': 'csharp',
+    'text/x-csrc': 'c',
+    'text/x-cuda': 'cuda',
+    'text/x-cxx': 'cpp',
+    'text/x-dart': 'dart',
+    'text/x-dlang': 'd',
+    'text/x-dockerfile': 'dockerfile',
+    'text/x-elisp': 'elisp',
+    'text/x-elixir': 'elixir',
+    'text/x-elm': 'elm',
+    'text/x-erlang': 'erlang',
+    'text/x-fish': 'fish',
+    'text/x-fortran': 'fortran',
+    'text/x-gdscript': 'gdscript',
+    'text/x-glsl': 'glsl',
+    'text/x-go': 'go',
+    'text/x-golang': 'go',
+    'text/x-hack': 'hack',
+    'text/x-haskell': 'haskell',
+    'text/x-hcl': 'hcl',
+    'text/x-hlsl': 'hlsl',
+    'text/x-ispc': 'ispc',
+    'text/x-java': 'java',
+    'text/x-java-source': 'java',
+    'text/x-julia': 'julia',
+    'text/x-kotlin': 'kotlin',
+    'text/x-latex': 'latex',
+    'text/x-llvm': 'llvm',
+    'text/x-lua': 'lua',
+    'text/x-luau': 'luau',
+    'text/x-magik': 'magik',
+    'text/x-makefile': 'make',
+    'text/x-markdown': 'markdown',
+    'text/x-matlab': 'matlab',
+    'text/x-meson': 'meson',
+    'text/x-nasm': 'asm',
+    'text/x-ninja': 'ninja',
+    'text/x-nix': 'nix',
+    'text/x-objchdr': 'objc',
+    'text/x-objcsrc': 'objc',
+    'text/x-objective-c': 'objc',
+    'text/x-ocaml': 'ocaml',
+    'text/x-octave': 'matlab',
+    'text/x-pascal': 'pascal',
+    'text/x-perl': 'perl',
+    'text/x-php': 'php',
+    'text/x-plsql': 'sql',
+    'text/x-po': 'po',
+    'text/x-pony': 'pony',
+    'text/x-powershell': 'powershell',
+    'text/x-prisma': 'prisma',
+    'text/x-properties': 'properties',
+    'text/x-proto': 'proto',
+    'text/x-psv': 'psv',
+    'text/x-puppet': 'puppet',
+    'text/x-purescript': 'purescript',
+    'text/x-python': 'python',
+    'text/x-python3': 'python',
+    'text/x-r': 'r',
+    'text/x-racket': 'racket',
+    'text/x-restructuredtext': 'rst',
+    'text/x-rst': 'rst',
+    'text/x-ruby': 'ruby',
+    'text/x-rust': 'rust',
+    'text/x-scala': 'scala',
+    'text/x-scheme': 'scheme',
+    'text/x-sh': 'bash',
+    'text/x-shell': 'bash',
+    'text/x-shellscript': 'bash',
+    'text/x-solidity': 'solidity',
+    'text/x-sparql': 'sparql',
+    'text/x-sql': 'sql',
+    'text/x-swift': 'swift',
+    'text/x-tcl': 'tcl',
+    'text/x-terraform': 'terraform',
+    'text/x-tex': 'latex',
+    'text/x-thrift': 'thrift',
+    'text/x-toml': 'toml',
+    'text/x-tsx': 'tsx',
+    'text/x-typescript': 'typescript',
+    'text/x-wgsl': 'wgsl',
+    'text/x-yaml': 'yaml',
+    'text/x-zig': 'zig',
+    'text/xml': 'xml',
+    'text/xtml': 'html',
+    'text/yaml': 'yaml',
+}
+
+
+FILE_SUFFIX_TO_LANGUAGES: OrderedDict[str, list[SupportedLanguage]] = OrderedDict({
+    'html.twig.js.css': ['twig'],
+    'requirements.txt': ['requirements'],
+    '.gitattributes': ['gitattributes'],
+    'CMakeLists.txt': ['cmake'],
+    '.bash_profile': ['bash'],
+    'Containerfile': ['dockerfile'],
+    'gitattributes': ['gitattributes'],
+    'linkerscript': ['linkerscript'],
+    'rebar.config': ['erlang'],
+    'GNUmakefile': ['make'],
+    'MANIFEST.in': ['pymanifest'],
+    '.gitignore': ['gitignore'],
+    'Dockerfile': ['dockerfile'],
+    'dockerfile': ['dockerfile'],
+    'properties': ['properties'],
+    '.XCompose': ['xcompose'],
+    '.fdignore': ['gitignore'],
+    '.rgignore': ['gitignore'],
+    'container': ['dockerfile'],
+    'html.twig': ['twig'],
+    'MAKEFILE': ['make'],
+    'Makefile': ['make'],
+    'XCompose': ['xcompose'],
+    'assembly': ['asm'],
+    'ldscript': ['linkerscript'],
+    'makefile': ['make'],
+    'squirrel': ['squirrel'],
+    '.bashrc': ['bash'],
+    '.ignore': ['gitignore'],
+    'Compose': ['xcompose'],
+    'Kconfig': ['kconfig'],
+    'app.src': ['erlang'],
+    'bbclass': ['bitbake'],
+    'chatito': ['chatito'],
+    'escript': ['erlang'],
+    'hs-boot': ['haskell'],
+    'jsonnet': ['jsonnet'],
+    'trigger': ['apex'],
+    'docker': ['dockerfile'],
+    'ebuild': ['bash'],
+    'eclass': ['bash'],
+    'go.mod': ['gomod'],
+    'luadoc': ['luadoc'],
+    'nimble': ['nim'],
+    'prisma': ['prisma'],
+    'qmldir': ['qmldir'],
+    'smithy': ['smithy'],
+    'sparql': ['sparql'],
+    'svelte': ['svelte'],
+    'tfvars': ['hcl', 'terraform'],
+    'thrift': ['thrift'],
+    'ungram': ['ungrammar'],
+    '.agda': ['agda'],
+    '.apex': ['apex'],
+    '.bash': ['bash'],
+    '.clar': ['clarity'],
+    '.cljc': ['clojure'],
+    '.cljs': ['clojure'],
+    '.conf': ['hyprlang'],
+    '.cpon': ['cpon'],
+    '.dart': ['dart'],
+    '.fish': ['fish'],
+    '.frag': ['glsl'],
+    '.glsl': ['glsl'],
+    '.hack': ['hack'],
+    '.hare': ['hare'],
+    '.heex': ['heex'],
+    '.hlsl': ['hlsl'],
+    '.html': ['html'],
+    '.ispc': ['ispc'],
+    '.java': ['java'],
+    '.json': ['json'],
+    '.lisp': ['commonlisp'],
+    '.llvm': ['llvm'],
+    '.luap': ['luap'],
+    '.luau': ['luau'],
+    '.neex': ['heex'],
+    '.nims': ['nim'],
+    '.objc': ['objc'],
+    '.odin': ['odin'],
+    '.pony': ['pony'],
+    '.psm1': ['powershell'],
+    '.purs': ['purescript'],
+    '.rego': ['rego'],
+    '.scss': ['scss'],
+    '.soql': ['apex'],
+    '.sosl': ['apex'],
+    '.test': ['test'],
+    '.toml': ['toml'],
+    '.twig': ['twig'],
+    '.vert': ['glsl'],
+    '.vhdl': ['vhdl'],
+    '.wgsl': ['wgsl'],
+    '.xslt': ['dtd', 'xml'],
+    '.yaml': ['yaml'],
+    '.yuck': ['yuck'],
+    'astro': ['astro'],
+    'bicep': ['bicep'],
+    'cairo': ['cairo'],
+    'capnp': ['capnp'],
+    'chatl': ['chatito'],
+    'cmake': ['cmake'],
+    'gleam': ['gleam'],
+    'janet': ['janet'],
+    'magik': ['magik'],
+    'meson': ['meson'],
+    'ninja': ['ninja'],
+    'rules': ['udev'],
+    'scala': ['scala'],
+    'sflog': ['apex'],
+    'smali': ['smali'],
+    'swift': ['swift'],
+    'v.mod': ['v'],
+    '.F90': ['fortran'],
+    '.F95': ['fortran'],
+    '.adb': ['ada'],
+    '.ads': ['ada'],
+    '.app': ['erlang'],
+    '.asm': ['asm'],
+    '.aux': ['latex'],
+    '.axb': ['netlinx'],
+    '.axi': ['netlinx'],
+    '.axs': ['netlinx'],
+    '.bib': ['bibtex'],
+    '.bzl': ['starlark'],
+    '.cer': ['pem'],
+    '.cjs': ['javascript'],
+    '.clj': ['clojure'],
+    '.cls': ['apex', 'latex'],
+    '.cpp': ['cpp'],
+    '.crt': ['pem'],
+    '.csr': ['pem'],
+    '.css': ['css'],
+    '.csv': ['csv', 'psv', 'tsv'],
+    '.cuh': ['cuda'],
+    '.cxx': ['cpp'],
+    '.dpr': ['pascal'],
+    '.dsp': ['make'],
+    '.dtd': ['dtd', 'xml'],
+    '.ejs': ['embeddedtemplate'],
+    '.elm': ['elm'],
+    '.erb': ['embeddedtemplate'],
+    '.erl': ['erlang'],
+    '.exs': ['elixir'],
+    '.f90': ['fortran'],
+    '.f95': ['fortran'],
+    '.fir': ['firrtl'],
+    '.fsh': ['glsl'],
+    '.hcl': ['hcl', 'terraform'],
+    '.hpp': ['cpp'],
+    '.hrl': ['erlang'],
+    '.hxx': ['cpp'],
+    '.ini': ['ini'],
+    '.ino': ['arduino'],
+    '.jsx': ['javascript'],
+    '.kdl': ['kdl'],
+    '.key': ['pem'],
+    '.kts': ['kotlin'],
+    '.lds': ['linkerscript'],
+    '.lib': ['netlinx'],
+    '.lpr': ['pascal'],
+    '.lua': ['lua'],
+    '.mak': ['make'],
+    '.mjs': ['javascript'],
+    '.mli': ['ocaml', 'ocaml_interface'],
+    '.mmd': ['mermaid'],
+    '.nim': ['nim'],
+    '.nix': ['nix'],
+    '.nqc': ['nqc'],
+    '.pas': ['pascal'],
+    '.pem': ['pem'],
+    '.pgn': ['pgn'],
+    '.php': ['php'],
+    '.pip': ['requirements'],
+    '.ps1': ['powershell'],
+    '.psv': ['csv', 'psv', 'tsv'],
+    '.qml': ['qmljs'],
+    '.rkt': ['racket'],
+    '.rng': ['dtd', 'xml'],
+    '.ron': ['ron'],
+    '.rst': ['rst'],
+    '.sbt': ['scala'],
+    '.scm': ['query', 'scheme'],
+    '.sol': ['solidity'],
+    '.sql': ['sql'],
+    '.sty': ['latex'],
+    '.sum': ['gosum'],
+    '.svg': ['dtd', 'xml'],
+    '.tal': ['uxntal'],
+    '.tcl': ['tcl'],
+    '.tex': ['latex'],
+    '.tsv': ['csv', 'psv', 'tsv'],
+    '.tsx': ['tsx', 'typescript'],
+    '.vhd': ['vhdl'],
+    '.vim': ['vim'],
+    '.vsh': ['glsl', 'v'],
+    '.xml': ['dtd', 'xml'],
+    '.xsd': ['dtd', 'xml'],
+    '.xsl': ['dtd', 'xml'],
+    '.yml': ['yaml'],
+    '.zig': ['zig'],
+    '.bb': ['bitbake', 'clojure'],
+    '.cc': ['cpp'],
+    '.cl': ['commonlisp'],
+    '.cs': ['csharp'],
+    '.cu': ['cuda'],
+    '.dd': ['d'],
+    '.el': ['elisp'],
+    '.ex': ['elixir'],
+    '.fc': ['func'],
+    '.gd': ['gdscript'],
+    '.gn': ['gn'],
+    '.go': ['go'],
+    '.hs': ['haskell'],
+    '.hx': ['haxe'],
+    '.jl': ['julia'],
+    '.js': ['javascript', 'tsx', 'typescript'],
+    '.kt': ['kotlin'],
+    '.ld': ['linkerscript'],
+    '.ll': ['llvm'],
+    '.md': ['markdown', 'markdown_inline'],
+    '.mk': ['make'],
+    '.ml': ['ocaml', 'ocaml_interface'],
+    '.pl': ['perl'],
+    '.pm': ['perl'],
+    '.po': ['po'],
+    '.pp': ['pascal', 'puppet'],
+    '.py': ['python'],
+    '.rb': ['ruby'],
+    '.re': ['re2c'],
+    '.rq': ['sparql'],
+    '.rs': ['rust'],
+    '.sh': ['bash'],
+    '.ss': ['scheme'],
+    '.td': ['tablegen'],
+    '.tf': ['hcl', 'terraform'],
+    '.tk': ['tcl'],
+    '.tm': ['tcl'],
+    '.ts': ['tsx', 'typescript'],
+    '.R': ['r'],
+    '.c': ['c'],
+    '.d': ['d'],
+    '.h': ['c', 'cpp', 'objc'],
+    '.m': ['matlab', 'objc'],
+    '.r': ['r'],
+    '.s': ['asm'],
+    '.t': ['perl'],
+    '.v': ['v'],
+})
