@@ -212,8 +212,77 @@ def:priv extract_peek_markdown(body: str) -> str {
212212}
213213
214214
215+ # Exact prefix python-markdown + codehilite (Pygments) emits for every
216+ # highlighted block. We splice the source language back onto each one — see
217+ # tag_code_languages.
218+ glob HL_MARKER = " <div class=\" highlight\" ><pre><span></span><code>" ;
219+
220+
221+ def :priv parse_info_lang (info : str ) -> str {
222+ # Normalize a fence info string into a bare language name:
223+ # "jac" -> "jac", "{.jac}" -> "jac", "python title=x" -> "python".
224+ parts = info.strip().split();
225+ if not parts {
226+ return " " ;
227+ }
228+ token = parts[0 ].strip(" {} " ).lstrip(" ." );
229+ if not token or (" =" in token) {
230+ return " " ;
231+ }
232+ return token;
233+ }
234+
235+
236+ def :priv extract_fence_langs (body : str ) -> list {
237+ # Languages of each fenced code block, in source order. codehilite emits
238+ # one HL_MARKER per fence in the same order, so the lists align 1:1.
239+ langs = [];
240+ in_fence = False ;
241+ fence = " " ;
242+ for raw in body.split(" \n " ) {
243+ line = raw.strip();
244+ if not in_fence {
245+ if line.startswith(" ```" ) or line.startswith(" ~~~" ) {
246+ in_fence = True ;
247+ fence = line[:3 ];
248+ langs.append(parse_info_lang(line[3 :]));
249+ }
250+ } elif line.startswith(fence) {
251+ in_fence = False ;
252+ fence = " " ;
253+ }
254+ }
255+ return langs;
256+ }
257+
258+
259+ def :priv tag_code_languages (html : str , body : str ) -> str {
260+ # codehilite drops the fence language from its output, leaving jac and
261+ # python blocks indistinguishable. Splice `language-<lang>` back onto the
262+ # wrapper div and inner <code> so the frontend can label + style each.
263+ langs = extract_fence_langs(body);
264+ parts = html.split(HL_MARKER );
265+ if len (parts) <= 1 {
266+ return html;
267+ }
268+ out = parts[0 ];
269+ for i in range (1 , len (parts)) {
270+ lang = " " ;
271+ if (i - 1 ) < len (langs) {
272+ lang = str (langs[i - 1 ]);
273+ }
274+ if lang {
275+ out = out + f " <div class= \" highlight language- { lang} \" ><pre><span></span><code class= \" language- { lang} \" > " + parts[i];
276+ } else {
277+ out = out + HL_MARKER + parts[i];
278+ }
279+ }
280+ return out;
281+ }
282+
283+
215284def :priv render_markdown (body : str ) -> str {
216- return md.markdown(
285+ html = md.markdown(
217286 body,
218287 extensions = [
219288 " fenced_code" ,
@@ -230,6 +299,7 @@ def:priv render_markdown(body: str) -> str {
230299 " toc" : {" permalink" : True , " permalink_class" : " headerlink" },
231300 },
232301 );
302+ return tag_code_languages(html, body);
233303}
234304
235305
@@ -1839,6 +1909,76 @@ def:priv find_open_edit_prs(token: str, slug: str) -> list {
18391909}
18401910
18411911
1912+ def :priv open_submission_prs (token : str ) -> list {
1913+ # ALL open PRs that ADD a NEW post (a docs/blog/posts/*.md that isn't already
1914+ # a live post), found by the file the PR adds rather than a body marker — so
1915+ # it picks up submissions opened through the portal AND directly on GitHub.
1916+ # Edits to an existing post add no new file, so they don't match here; they
1917+ # are surfaced separately by open_edit_prs_by_slug. Mirrors that function's
1918+ # file-based detection so the review queue and the open-edits list stay
1919+ # consistent about direct-GitHub PRs.
1920+ out: list = [];
1921+ path_to_slug = post_path_to_slug();
1922+ try {
1923+ prs = requests.get(
1924+ f " { GH_API } /repos/ { gh_repo()} /pulls " ,
1925+ headers = gh_headers(token),
1926+ params = {" state" : " open" , " per_page" : 100 , " sort" : " created" , " direction" : " desc" },
1927+ timeout = 30 ,
1928+ ).json();
1929+ } except Exception {
1930+ return out;
1931+ }
1932+ if not isinstance (prs, list ) {
1933+ return out;
1934+ }
1935+ for pr in prs {
1936+ n = int (pr.get(" number" , 0 ));
1937+ try {
1938+ files = requests.get(
1939+ f " { GH_API } /repos/ { gh_repo()} /pulls/ { n} /files " ,
1940+ headers = gh_headers(token), params = {" per_page" : 100 }, timeout = 30 ,
1941+ ).json();
1942+ } except Exception {
1943+ continue ;
1944+ }
1945+ if not isinstance (files, list ) {
1946+ continue ;
1947+ }
1948+ adds_new_post = False ;
1949+ for f in files {
1950+ fn = str (f.get(" filename" , " " ));
1951+ status = str (f.get(" status" , " " ));
1952+ name = fn.split(" /" )[- 1 ];
1953+ if (
1954+ fn.startswith(f " { POSTS_DIR } / " )
1955+ and fn.endswith(" .md" )
1956+ and not name.startswith(" _" )
1957+ and fn not in path_to_slug
1958+ and status == " added"
1959+ ) {
1960+ adds_new_post = True ;
1961+ break ;
1962+ }
1963+ }
1964+ if not adds_new_post {
1965+ continue ;
1966+ }
1967+ user = pr.get(" user" );
1968+ author = str (user.get(" login" , " " )) if isinstance (user, dict ) else " " ;
1969+ out.append({
1970+ " title" : str (pr.get(" title" , " " )),
1971+ " slug" : " " ,
1972+ " author" : author,
1973+ " pr_number" : n,
1974+ " pr_url" : str (pr.get(" html_url" , " " )),
1975+ " created_at" : str (pr.get(" created_at" , " " )),
1976+ });
1977+ }
1978+ return out;
1979+ }
1980+
1981+
18421982def :priv build_edited_markdown (
18431983 original_content : str , title : str , categories : list , description : str , body : str
18441984) -> str {
@@ -2363,35 +2503,7 @@ walker:priv ListSubmissions {
23632503 report {" ok" : False , " error" : " reviewer access required" };
23642504 return ;
23652505 }
2366- try {
2367- q = f ' repo: { gh_repo()} type:pr state:open " { SUBMISSION_TAG } " in:body ' ;
2368- r = requests.get(
2369- f " { GH_API } /search/issues " ,
2370- headers = gh_headers(gh_token),
2371- params = {" q" : q, " per_page" : 50 , " sort" : " created" , " order" : " desc" },
2372- timeout = 30 ,
2373- );
2374- data = r.json();
2375- } except Exception as e {
2376- report {" ok" : False , " error" : f " could not load queue: { e} " };
2377- return ;
2378- }
2379- items = data.get(" items" , []) if isinstance (data, dict ) else [];
2380- out = [];
2381- for it in items {
2382- body = str (it.get(" body" , " " ) or " " );
2383- m = re.search(r " slug=( [a-z0-9- ]+ ) " , body);
2384- bm = re.search(r " by=( [A-Za-z0-9- ]+ ) " , body);
2385- out.append({
2386- " title" : str (it.get(" title" , " " )),
2387- " slug" : m.group(1 ) if m else " " ,
2388- " author" : bm.group(1 ) if bm else " " ,
2389- " pr_number" : it.get(" number" ),
2390- " pr_url" : str (it.get(" html_url" , " " )),
2391- " created_at" : str (it.get(" created_at" , " " )),
2392- });
2393- }
2394- report {" ok" : True , " submissions" : out};
2506+ report {" ok" : True , " submissions" : open_submission_prs(gh_token)};
23952507 }
23962508}
23972509
0 commit comments