This commit is contained in:
Michael Zhang 2024-09-12 19:43:27 -05:00
parent 7d14703242
commit 4d8e83a74a
7 changed files with 117 additions and 54 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -1,22 +1,32 @@
{ {
"name": "remark-agda", "name": "remark-agda",
"version": "0.0.1",
"main": "dist/index.js",
"module": "src/index.ts", "module": "src/index.ts",
"types": "dist/index.d.ts",
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.0", "@biomejs/biome": "^1.9.0",
"@types/bun": "latest", "@types/bun": "latest",
"rehype-raw": "^7.0.0",
"rehype-stringify": "^10.0.0", "rehype-stringify": "^10.0.0",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0", "remark-rehype": "^11.1.0",
"vfile": "^6.0.3" "to-vfile": "^8.0.0",
"vfile": "^6.0.3",
"vfile-reporter": "^8.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"hast": "^1.0.0",
"hast-util-from-html": "^2.0.2", "hast-util-from-html": "^2.0.2",
"unified": "^11.0.5", "unified": "^11.0.5",
"unist": "^0.0.1",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"
}, },
"trustedDependencies": ["@biomejs/biome"] "trustedDependencies": ["@biomejs/biome"],
"files": ["dist/index.js", "dist/index.d.ts"]
} }

5
scripts/prepare-release.sh Executable file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
mkdir -p dist/
bun build --target node src/index.ts > dist/index.js
bunx tsc

View file

@ -1,6 +1,6 @@
import { join, parse } from "node:path"; import { join, parse } from "node:path";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { spawnSync, spawn } from "node:child_process"; import { spawnSync } from "node:child_process";
import { import {
readdir, readdir,
mkdtemp, mkdtemp,
@ -10,12 +10,13 @@ import {
writeFile, writeFile,
} from "node:fs/promises"; } from "node:fs/promises";
import { mkdirSync } from "node:fs"; import { mkdirSync } from "node:fs";
import type { Plugin } from "unified"; import type { Plugin } from "unified";
import { visit } from "unist-util-visit"; import { visit } from "unist-util-visit";
import { fromMarkdown } from "mdast-util-from-markdown"; import { fromMarkdown } from "mdast-util-from-markdown";
import { fromHtml } from "hast-util-from-html"; import { fromHtml } from "hast-util-from-html";
import { toHtml } from "hast-util-to-html"; import { toHtml } from "hast-util-to-html";
import type { RootContent } from "hast";
import type { Node as UnistNode } from "unist";
export interface RemarkAgdaOptions { export interface RemarkAgdaOptions {
/** Place to output the HTML files */ /** Place to output the HTML files */
@ -31,7 +32,7 @@ export interface RemarkAgdaOptions {
extraAgdaFlags?: string[]; extraAgdaFlags?: string[];
} }
const remarkAgda: Plugin<[RemarkAgdaOptions]> = ({ export const remarkAgda: Plugin<[RemarkAgdaOptions]> = ({
agdaBin, agdaBin,
extraAgdaFlags, extraAgdaFlags,
destDir, destDir,
@ -48,19 +49,14 @@ const remarkAgda: Plugin<[RemarkAgdaOptions]> = ({
} }
const path = history[history.length - 1]; const path = history[history.length - 1];
// console.log("path", path);
// if (!(path.endsWith(".lagda.md") || path.endsWith(".agda"))) return; // if (!(path.endsWith(".lagda.md") || path.endsWith(".agda"))) return;
// console.log("AGDA:processing path", path); // console.log("AGDA:processing path", path);
const agdaOutDir = await mkdtemp(join(tmpdir(), "agdaRender.")); const agdaOutDir = await mkdtemp(join(tmpdir(), "agdaRender."));
// const agdaOutDir = join(tempDir, "output"); // const agdaOutDir = join(tempDir, "output");
const agdaOutFilename = parse(path).base.replace(/\.lagda.md$/, ".md"); const agdaOutFilename = parse(path).base.replace(/\.lagda.md$/, ".md");
const agdaOutFile = join(agdaOutDir, agdaOutFilename); const agdaOutFile = join(agdaOutDir, agdaOutFilename);
console.log("looking for file", agdaOutFile);
// mkdirSync(agdaOutDir, { recursive: true }); // mkdirSync(agdaOutDir, { recursive: true });
const childOutput = spawnSync(
const childOutput = await spawnSync(
agdaBin ?? "agda", agdaBin ?? "agda",
[ [
"--html", "--html",
@ -94,7 +90,6 @@ const remarkAgda: Plugin<[RemarkAgdaOptions]> = ({
// console.error(childOutput.stdout?.toString()); // console.error(childOutput.stdout?.toString());
// console.error(childOutput.stderr?.toString()); // console.error(childOutput.stderr?.toString());
// console.error("--AGDA OUTPUT--"); // console.error("--AGDA OUTPUT--");
const referencedFiles = new Set(); const referencedFiles = new Set();
const writtenFiles = await readdir(agdaOutDir); const writtenFiles = await readdir(agdaOutDir);
@ -118,48 +113,70 @@ const remarkAgda: Plugin<[RemarkAgdaOptions]> = ({
const htmlname = parse(path).base.replace(/\.lagda.md/, ".html"); const htmlname = parse(path).base.replace(/\.lagda.md/, ".html");
const doc = await readFile(agdaOutFile); const doc = await readFile(agdaOutFile, { encoding: "utf-8" });
// This is the post-processed markdown with HTML code blocks replacing the Agda code blocks // This is the post-processed markdown with HTML code blocks replacing the Agda code blocks
const tree2 = fromMarkdown(doc); const tree2 = fromMarkdown(doc);
const collectedCodeBlocks: RootContent[] = []; const collectedCodeBlocks: string[] = [];
visit(tree2, "html", (node) => { visit(tree2, "html", (node) => {
const html = fromHtml(node.value, { fragment: true }); const html = fromHtml(node.value, { fragment: true });
const firstChild: RootContent = html.children[0]!;
visit(html, "element", (node) => { visit(html, "element", (node) => {
if (node.tagName !== "a") return; if (node.tagName !== "a") return;
if (node.properties.href) { if (typeof node.properties.href === "string") {
// Trim off end // Trim off end
const [href, hash, ...rest] = node.properties.href.split("#"); const [href, hash, ...rest] = node.properties.href.split("#");
if (rest.length > 0) throw new Error("come look at this"); if (rest.length > 0) throw new Error("come look at this");
if (href === htmlname) node.properties.href = `#${hash}`; if (href === htmlname) node.properties.href = `#${hash}`;
if (referencedFiles.has(href)) { // TODO: Transform
node.properties.href = `${base}generated/agda/${href}${hash ? `#${hash}` : ""}`; // if (referencedFiles.has(href)) {
node.properties.target = "_blank"; // node.properties.href = `${base}generated/agda/${href}${hash ? `#${hash}` : ""}`;
} // node.properties.target = "_blank";
// }
} }
}); });
if (!firstChild?.properties?.className?.includes("Agda")) return; while (true) {
if (html.children.length > 0) {
const firstChild: RootContent = html.children[0];
const stringContents = toHtml(firstChild); if (firstChild.type !== "element") break;
collectedCodeBlocks.push({
contents: stringContents, const className = firstChild.properties.className;
});
// @ts-ignore TODO: Fix this
if (!className?.includes("Agda")) break;
const stringContents = toHtml(firstChild);
collectedCodeBlocks.push(stringContents);
}
break;
}
}); });
console.log(`Collected ${collectedCodeBlocks.length} blocks!`);
let idx = 0; let idx = 0;
visit(tree, "code", (node) => {
visit(tree, "code", (node: UnistNode) => {
// Make sure it's either null (which gets interpreted as agda), or agda
// @ts-ignore
if (!(node.lang === null || node.lang === "agda")) return; if (!(node.lang === null || node.lang === "agda")) return;
// node.type = "html";
node.type = "html"; node.type = "html";
node.value = collectedCodeBlocks[idx].contents;
// @ts-ignore
node.value = collectedCodeBlocks[idx];
console.log(node);
idx += 1; idx += 1;
}); });
}; };

View file

@ -1,3 +1,10 @@
# Commutativity of addition
This document shows how to prove commutativity of addition on natural numbers.
<details>
<summary>Imports</summary>
``` ```
module Simple where module Simple where
@ -5,23 +12,39 @@ open import Agda.Primitive
open import Relation.Binary.PropositionalEquality.Core open import Relation.Binary.PropositionalEquality.Core
variable variable
l : Level l : Level
```
</details>
Declare our data structures:
```
data : Set where data : Set where
zero : zero :
suc : suc :
```
Define addition:
```
_+_ : _+_ :
zero + b = b zero + b = b
suc a + b = suc (a + b) suc a + b = suc (a + b)
```
Prove commutativity:
```
+-comm : (m n : ) → m + n ≡ n + m +-comm : (m n : ) → m + n ≡ n + m
+-comm zero n = lemma n where +-comm zero n = lemma n where
lemma : (n : ) → n ≡ n + zero lemma : (n : ) → n ≡ n + zero
lemma zero = refl lemma zero = refl
lemma (suc n) = cong suc (lemma n) lemma (suc n) = cong suc (lemma n)
+-comm (suc m) n = trans (cong suc (+-comm m n)) (sym (lemma n m)) where +-comm (suc m) n = trans (cong suc (+-comm m n)) (sym (lemma n m)) where
lemma : (m n : ) → m + suc n ≡ suc (m + n) lemma : (m n : ) → m + suc n ≡ suc (m + n)
lemma zero n = refl lemma zero n = refl
lemma (suc m) n = cong suc (lemma m n) lemma (suc m) n = cong suc (lemma m n)
``` ```
That's it!

View file

@ -1,22 +1,22 @@
import { test } from "bun:test"; import { test } from "bun:test";
import { resolve, dirname, join } from "node:path"; import { resolve, dirname, join } from "node:path";
import { unified } from "unified"; import { unified } from "unified";
import remarkAgda from "../src";
import remarkParse from "remark-parse"; import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype"; import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify"; import rehypeStringify from "rehype-stringify";
import { VFile } from "vfile"; import rehypeRaw from "rehype-raw";
import { read } from "to-vfile";
import remarkAgda, { type RemarkAgdaOptions } from "../src";
test("simple case", async () => { test("simple case", async () => {
const file = join(dirname(import.meta.path), "Simple.lagda.md"); const file = join(dirname(import.meta.path), "Simple.lagda.md");
const vfile = new VFile({ path: file }); const vfile = await read(file);
const result = await unified() const options: RemarkAgdaOptions = {
.use(remarkParse) destDir: join(dirname(import.meta.path), "results"),
.use(remarkAgda, { transformHtml: (src) => {
destDir: join(dirname(import.meta.path), "results"), return `
transformHtml: (src) => {
return `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@ -29,10 +29,14 @@ test("simple case", async () => {
</body> </body>
</html> </html>
`; `;
}, },
}) };
.use(remarkRehype)
await unified()
.use(remarkParse)
.use(remarkAgda, options)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeRaw)
.use(rehypeStringify) .use(rehypeStringify)
.process(vfile); .process(vfile);
console.log("result", result);
}); });

View file

@ -12,16 +12,20 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"noEmit": true,
// Best practices // Best practices
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"emitDeclarationOnly": true,
// Some stricter flags (disabled by default) // Some stricter flags (disabled by default)
"noUnusedLocals": false, "noUnusedLocals": false,
"noUnusedParameters": false, "noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false "noPropertyAccessFromIndexSignature": false,
}
"outDir": "dist",
"declaration": true
},
"files": ["src/index.ts"]
} }