Skip to content

Commit 8f71fea

Browse files
committed
parse source academy docs instead of converting ace editor docs
1 parent c71d630 commit 8f71fea

File tree

11 files changed

+6819
-1389
lines changed

11 files changed

+6819
-1389
lines changed

package-lock.json

Lines changed: 711 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
]
5757
},
5858
"dependencies": {
59+
"jsdom": "^26.0.0",
60+
"turndown": "^7.2.0",
5961
"vscode-languageserver": "^9.0.1"
6062
},
6163
"scripts": {

server/src/docs/build_docs.mjs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// @ts-check
2+
3+
import fs from 'fs/promises';
4+
import pathlib from "path";
5+
import TurndownService from 'turndown';
6+
import { JSDOM } from 'jsdom';
7+
import patches from "./patches.json" with { type: "json" };
8+
9+
const CONST_DECL = "const";
10+
const FUNC_DECL = "func";
11+
12+
const BASE_URL = "https://docs.sourceacademy.org"
13+
const SRC_FILENAME = "global.html"
14+
const OUT_DIR = "./"
15+
16+
const TARGETS = [
17+
"source_1",
18+
"source_2",
19+
"source_3",
20+
"source_4",
21+
]
22+
23+
const FINAL_TARGET = "source";
24+
25+
const turndownService = new TurndownService();
26+
27+
function newTitleNode(title, document) {
28+
const node = document.createElement('h4');
29+
const text = document.createTextNode(title);
30+
node.appendChild(text);
31+
return node;
32+
}
33+
34+
function buildDescriptionMarkdown(div) {
35+
return turndownService.turndown(div.outerHTML.replace('/\n+/', '\n'));
36+
}
37+
38+
function processConstant(names, element, document) {
39+
const header = element.getElementsByTagName('h4')[0];
40+
const rawName = header.textContent;
41+
const fields = rawName.split(' ').slice(1)
42+
43+
let title = fields.join('');
44+
const name = header.getAttribute('id');
45+
if (!title) {
46+
title = name;
47+
}
48+
49+
const titleNode = newTitleNode(title, document);
50+
const descriptionNode = element.getElementsByClassName('description')[0];
51+
52+
const descriptionDiv = document.createElement('div');
53+
descriptionDiv.appendChild(titleNode);
54+
descriptionDiv.appendChild(descriptionNode);
55+
const markdown = buildDescriptionMarkdown(descriptionDiv);
56+
57+
names.push({ label: name, title, description: markdown, meta: CONST_DECL });
58+
}
59+
60+
function processFunction(names, element, document) {
61+
const header = element.getElementsByTagName('h4')[0];
62+
const title = header.textContent;
63+
const name = header.getAttribute('id');
64+
65+
const titleNode = newTitleNode(title, document);
66+
const descriptionNode = element.getElementsByClassName('description')[0];
67+
68+
const descriptionDiv = document.createElement('div');
69+
descriptionDiv.appendChild(titleNode);
70+
descriptionDiv.appendChild(descriptionNode);
71+
const html = buildDescriptionMarkdown(descriptionDiv);
72+
73+
const params = (Object.keys(patches["rename_params"])).includes(name) ? patches["rename_params"][name] : [...title.matchAll(/\w+\(([^)]*)\)/g)][0][1].split(",").map(s => s.trim());
74+
const autocomplete = { label: name, title, description: html, meta: FUNC_DECL, parameters: params[0] === '' ? [] : params };
75+
76+
if (Object.keys(patches["optional_params"]).includes(name))
77+
autocomplete["optional_params"] = patches["optional_params"][name];
78+
if (patches["hasRestElement"].includes(name))
79+
autocomplete["hasRestElement"] = true;
80+
81+
82+
names.push(autocomplete);
83+
}
84+
85+
async function processDirGlobals(global, target, index) {
86+
const url = `${BASE_URL}/${target}/${SRC_FILENAME}`;
87+
let document;
88+
try {
89+
const contents = await (await fetch(url)).text();
90+
document = new JSDOM(contents.toString()).window.document;
91+
} catch (err) {
92+
console.error(url, "failed", err);
93+
return err;
94+
}
95+
96+
const names = [];
97+
98+
const constants = document.getElementsByClassName("constant-entry");
99+
Array.prototype.forEach.call(constants, ele => processConstant(names, ele, document));
100+
101+
const functions = document.getElementsByClassName('function-entry')
102+
Array.prototype.forEach.call(functions, ele => processFunction(names, ele, document));
103+
104+
const outFile = pathlib.join(OUT_DIR, target + '.json');
105+
await fs.writeFile(outFile, JSON.stringify(names, null, 2), 'utf-8')
106+
global[index] = (names);
107+
return undefined
108+
}
109+
110+
await fs.mkdir(OUT_DIR, { recursive: true })
111+
112+
const global = [];
113+
// Exit with error code if the there was some error
114+
const errors = await Promise.all(TARGETS.map((x, i) => processDirGlobals(global, x, i)));
115+
if (errors.find(each => each !== undefined)) process.exit(1);
116+
const outFile = pathlib.join(OUT_DIR, FINAL_TARGET + '.json');
117+
await fs.writeFile(outFile, JSON.stringify(global, null, 2), 'utf-8')
118+
119+
console.log('Finished processing autocomplete documentation')

server/src/docs/convertJsonDocs.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

server/src/docs/requirements.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

server/src/docs/source.json

Lines changed: 3236 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)