1#!/usr/bin/env nix-shell
2#!nix-shell ./update-shell.nix -i python
3
4import json
5import logging
6import os
7import subprocess
8from concurrent.futures import ThreadPoolExecutor
9from pathlib import Path
10
11import requests
12
13log = logging.getLogger("vim-updater")
14
15NURR_JSON_URL = (
16 "https://raw.githubusercontent.com/nvim-neorocks/nurr/main/tree-sitter-parsers.json"
17)
18
19
20def generate_grammar(lang, parser_info):
21 """Generate grammar for a language based on the parser info"""
22 if "install_info" not in parser_info:
23 log.warning(f"Parser {lang} does not have install_info, skipping")
24 return ""
25
26 install_info = parser_info["install_info"]
27
28 url = install_info["url"]
29 rev = install_info["revision"]
30
31 generated = f""" {lang} = buildGrammar {{
32 language = "{lang}";
33 version = "0.0.0+rev={rev[:7]}";
34 src = """
35
36 generated += subprocess.check_output(
37 ["nurl", url, rev, "--indent=4"], text=True
38 )
39 generated += ";"
40
41 location = install_info.get("location", "")
42 if location:
43 generated += f"""
44 location = "{location}";"""
45
46 if install_info.get("generate", False):
47 generated += """
48 generate = true;"""
49
50 generated += f"""
51 meta.homepage = "{url}";
52 }};
53"""
54
55 return generated
56
57
58def fetch_nurr_parsers():
59 """Fetch the parser information from nurr repository"""
60 log.info("Fetching parser data from %s", NURR_JSON_URL)
61
62 headers = {}
63 github_token = os.environ.get("GITHUB_TOKEN")
64 if github_token:
65 log.info("Using GITHUB_TOKEN for authentication")
66 headers["Authorization"] = f"token {github_token}"
67 else:
68 log.warning("No GITHUB_TOKEN found. GitHub API requests may be rate-limited.")
69
70 response = requests.get(NURR_JSON_URL, headers=headers, timeout=30)
71 response.raise_for_status()
72 data = response.json()
73
74 try:
75 parsers = data["parsers"]
76 except KeyError:
77 raise ValueError(
78 "Unexpected response from NURR:\n" + json.dumps(data, indent=2)
79 )
80 log.info(f"Successfully fetched {len(parsers)} parsers")
81 return parsers
82
83
84def process_parser_info(parser_info):
85 """Process a single parser info entry and generate grammar for it"""
86 return generate_grammar(parser_info["lang"], parser_info)
87
88
89def update_grammars():
90 """Update grammar definitions using nurr's parser information"""
91 parsers_info = fetch_nurr_parsers()
92
93 generated_file = """# generated by pkgs/applications/editors/vim/plugins/utils/nvim-treesitter/update.py
94# Using parser data from https://github.com/nvim-neorocks/nurr/blob/main/tree-sitter-parsers.json
95
96{
97 buildGrammar,
98 """
99
100 nurl_output = subprocess.check_output(["nurl", "-Ls", ","], text=True).strip()
101 indented_output = nurl_output.replace(",", ",\n ")
102 generated_file += indented_output
103 generated_file += """,
104}:
105
106{
107"""
108
109 # Process parsers in parallel for better performance
110 with ThreadPoolExecutor(max_workers=5) as executor:
111 for generated in executor.map(process_parser_info, parsers_info):
112 generated_file += generated
113
114 generated_file += "}\n"
115 return generated_file
116
117
118if __name__ == "__main__":
119 logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
120
121 generated = update_grammars()
122 output_path = Path(__file__).parent.parent.parent / "nvim-treesitter/generated.nix"
123 log.info("Writing output to %s", output_path)
124 with open(output_path, "w") as f:
125 f.write(generated)
126 log.info("Successfully updated grammar definitions")