Skip to content

Commit

Permalink
Environments (#122)
Browse files Browse the repository at this point in the history
* Implement pretext environments for reasonably named latex environments.
  • Loading branch information
oscarlevin authored Nov 8, 2024
1 parent a2a2a5f commit f1edf00
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 8 deletions.
6 changes: 1 addition & 5 deletions packages/unified-latex-to-pretext/libs/author-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ export function gatherAuthorInfo(ast: Ast.Ast, file: VFile): AuthorInfo[] {
authorList.push(authorEmail);
} else if (match.macro(node, "affil")) {
const message = createVFileMessage(node);
file.message(
message,
message.place,
"latex-to-pretext:warning"
)
file.message(message, message.place, "latex-to-pretext:warning");
}
});
return authorList;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { htmlLike } from "@unified-latex/unified-latex-util-html-like";
import * as Ast from "@unified-latex/unified-latex-types";
import { getNamedArgsContent } from "@unified-latex/unified-latex-util-arguments";
import {
getArgsContent,
getNamedArgsContent,
} from "@unified-latex/unified-latex-util-arguments";
import { match } from "@unified-latex/unified-latex-util-match";
import { wrapPars } from "../wrap-pars";
import { VisitInfo } from "@unified-latex/unified-latex-util-visit";
Expand Down Expand Up @@ -91,6 +94,55 @@ function enumerateFactory(parentTag = "ol") {
};
}

/**
* Factory function that builds html-like macros wrapping the contents of an environment.
* Statement tags are added around the contents of the environment if requested.
*/
function envFactory(
tag: string,
requiresStatementTag: boolean = false,
warningMessage: string = "",
attributes?: Record<string, string>
): (env: Ast.Environment, info: VisitInfo, file?: VFile) => Ast.Macro {
return (env, info, file) => {
// add a warning message to the file if needed
if (warningMessage && file) {
const message = makeWarningMessage(env, warningMessage, "env-subs");
file.message(message, message.place, message.source);
}

// Wrap content of the environment in paragraph tags
let content = wrapPars(env.content);

// Add a statement around the contents of the environment if requested.
if (requiresStatementTag) {
content = [
htmlLike({
tag: "statement",
content: content,
}),
];
}

// Add a title tag if the environment has a title
const args = getArgsContent(env);
if (args[0]) {
content.unshift(
htmlLike({
tag: "title",
content: args[0] || [],
})
);
}

// Put it all together
return htmlLike({
tag: tag,
content: content,
});
};
}

/**
* Remove the env environment by returning the content in env only.
*/
Expand All @@ -117,8 +169,9 @@ export const environmentReplacements: Record<
node: Ast.Environment,
info: VisitInfo,
file?: VFile
) => Ast.Macro | Ast.String | Ast.Environment | Ast.Node[]
) => Ast.Node | Ast.Node[]
> = {
// TODO: add additional envs like theorem, etc.
enumerate: enumerateFactory("ol"),
itemize: enumerateFactory("ul"),
center: removeEnv,
Expand All @@ -129,4 +182,93 @@ export const environmentReplacements: Record<
content: env.content,
});
},
...genEnvironmentReplacements(),
};

function genEnvironmentReplacements() {
let reps: Record<
string,
(node: Ast.Environment, info: VisitInfo, file?: VFile) => Ast.Node
> = {};
// First, a long list of pretext environments and their aliases.
const envAliases: Record<
string,
{ requiresStatment: boolean; aliases: string[] }
> = {
abstract: { requiresStatment: false, aliases: ["abs", "abstr"] },
acknowledgement: { requiresStatment: false, aliases: ["ack"] },
algorithm: { requiresStatment: true, aliases: ["algo", "alg"] },
assumption: { requiresStatment: true, aliases: ["assu", "ass"] },
axiom: { requiresStatment: true, aliases: ["axm"] },
claim: { requiresStatment: true, aliases: ["cla"] },
conjecture: {
requiresStatment: true,
aliases: ["con", "conj", "conjec"],
},
construction: { requiresStatment: false, aliases: [] },
convention: { requiresStatment: false, aliases: ["conv"] },
corollary: {
requiresStatment: true,
aliases: ["cor", "corr", "coro", "corol", "corss"],
},
definition: {
requiresStatment: true,
aliases: ["def", "defn", "dfn", "defi", "defin", "de"],
},
example: {
requiresStatment: true,
aliases: ["exam", "exa", "eg", "exmp", "expl", "exm"],
},
exercise: { requiresStatment: true, aliases: ["exer", "exers"] },
exploration: { requiresStatment: false, aliases: [] },
fact: { requiresStatment: true, aliases: [] },
heuristic: { requiresStatment: true, aliases: [] },
hypothesis: { requiresStatment: true, aliases: ["hyp"] },
identity: { requiresStatment: true, aliases: ["idnty"] },
insight: { requiresStatment: false, aliases: [] },
investigation: { requiresStatment: false, aliases: [] },
lemma: {
requiresStatment: true,
aliases: ["lem", "lma", "lemm", "lm"],
},
notation: {
requiresStatment: false,
aliases: ["no", "nota", "ntn", "nt", "notn", "notat"],
},
note: { requiresStatment: false, aliases: ["notes"] },
observation: { requiresStatment: false, aliases: ["obs"] },
principle: { requiresStatment: true, aliases: [] },
problem: { requiresStatment: true, aliases: ["prob", "prb"] },
project: { requiresStatment: false, aliases: [] },
proof: { requiresStatment: false, aliases: ["pf", "prf", "demo"] },
proposition: {
requiresStatment: true,
aliases: ["prop", "pro", "prp", "props"],
},
question: {
requiresStatment: true,
aliases: ["qu", "ques", "quest", "qsn"],
},
remark: {
requiresStatment: false,
aliases: ["rem", "rmk", "rema", "bem", "subrem"],
},
task: { requiresStatment: true, aliases: [] },
theorem: {
requiresStatment: true,
aliases: ["thm", "theo", "theor", "thmss", "thrm"],
},
warning: { requiresStatment: false, aliases: ["warn", "wrn"] },
};
// For each environment PreTeXt has, we create entries for `environmentReplacements` using all reasonable aliases
const exapandedEnvAliases = Object.entries(envAliases).flatMap(
([env, spec]) => [
[env, envFactory(env, spec.requiresStatment)],
...spec.aliases.map((name) => [
name,
envFactory(env, spec.requiresStatment),
]),
]
);
return Object.fromEntries(exapandedEnvAliases);
}
4 changes: 3 additions & 1 deletion packages/unified-latex-to-pretext/tests/author-info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ describe("unified-latex-to-pretext:author-info", () => {
.use(xmlCompilePlugin)
.runSync({ type: "root", children: [toXast(rendered)].flat() });
expect(normalizeHtml(toXml(xxx1))).toEqual(
normalizeHtml("<author><institution>Affiliation</institution></author>")
normalizeHtml(
"<author><institution>Affiliation</institution></author>"
)
);

sample =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,4 +357,34 @@ describe("unified-latex-to-pretext:unified-latex-to-pretext", () => {
normalizeHtml(`<yyy>a</yyy><yyy><yyy-child>b</yyy-child>c</yyy>`)
);
});
it("converts theorem-like environments that have statements in ptx", () => {
html = process(`\\begin{lemma}\na\n\nb\n\\end{lemma}`);
expect(normalizeHtml(html)).toEqual(
normalizeHtml(
`<lemma><statement><p>a</p><p>b</p></statement></lemma>`
)
);
});
it("converts dfn to definition block", () => {
html = process(`\\begin{dfn}\na\n\\end{dfn}`);
expect(normalizeHtml(html)).toEqual(
normalizeHtml(
`<definition><statement><p>a</p></statement></definition>`
)
);
});
it("Gives a theorem a title", () => {
html = process(`\\begin{theorem}[My Theorem]\na\n\nb\n\\end{theorem}`);
expect(normalizeHtml(html)).toEqual(
normalizeHtml(
`<theorem><title>My Theorem</title><statement><p>a</p><p>b</p></statement></theorem>`
)
);
});
it("Gives an environment without statement a title", () => {
html = process(`\\begin{remark}[My remark]\na\n\\end{remark}`);
expect(normalizeHtml(html)).toEqual(
normalizeHtml(`<remark><title>My remark</title><p>a</p></remark>`)
);
});
});

0 comments on commit f1edf00

Please sign in to comment.