Skip to content

Commit

Permalink
Merge branch 'siefkenj:pretext' into pretext
Browse files Browse the repository at this point in the history
  • Loading branch information
renee-k authored Aug 16, 2024
2 parents c359495 + 27e9fbf commit e09f4cc
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 11 deletions.
8 changes: 7 additions & 1 deletion packages/support-tables/ligature-macros.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
["\\\" w", ""],
["\\\" y", "ÿ"],
["\\\" Y", "Ÿ"],
["\\' i", "í"],
["\\' A", "Á"],
["\\' a", "á"],
["\\' C", "Ć"],
Expand All @@ -51,16 +50,23 @@
["\\' G", "Ǵ"],
["\\' g", "ǵ"],
["\\' I", "Í"],
["\\' i", "í"],
["\\' \\i", "í"],
["\\' \\j", ""],
["\\' K", ""],
["\\' k", ""],
["\\' L", "Ĺ"],
["\\' \\L", "Ł́"],
["\\' l", "ĺ"],
["\\' \\l", "ł́"],
["\\' M", ""],
["\\' m", "ḿ"],
["\\' N", "Ń"],
["\\' n", "ń"],
["\\' O", "Ó"],
["\\' \\O", "Ǿ"],
["\\' o", "ó"],
["\\' \\o", "ǿ"],
["\\' P", ""],
["\\' p", ""],
["\\' R", "Ŕ"],
Expand Down
3 changes: 3 additions & 0 deletions packages/unified-latex-ctan/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as amsart from "./package/amsart";
import * as cleveref from "./package/cleveref";
import * as exam from "./package/exam";
import * as geometry from "./package/geometry";
Expand All @@ -20,6 +21,7 @@ import * as multicol from "./package/multicol";
* the standard macros for LaTeX.
*/
export const macroInfo = {
amsart: amsart.macros,
cleveref: cleveref.macros,
exam: exam.macros,
geometry: geometry.macros,
Expand All @@ -43,6 +45,7 @@ export const macroInfo = {
* the standard environments for LaTeX.
*/
export const environmentInfo = {
amsart: amsart.environments,
cleveref: cleveref.environments,
exam: exam.environments,
geometry: geometry.environments,
Expand Down
1 change: 1 addition & 0 deletions packages/unified-latex-ctan/package/amsart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./provides";
33 changes: 33 additions & 0 deletions packages/unified-latex-ctan/package/amsart/provides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
MacroInfoRecord,
EnvInfoRecord,
} from "@unified-latex/unified-latex-types";

export const macros: MacroInfoRecord = {
author: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
address: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
curraddr: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
email: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
title: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
urladdr: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
}
}

export const environments: EnvInfoRecord = {};
11 changes: 1 addition & 10 deletions packages/unified-latex-ctan/package/latex2e/provides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const macros: MacroInfoRecord = {
renderInfo: { breakAround: true, pgfkeysArgs: true },
},
discretionary: { signature: "m m m" },
hyphenation: { signature: "m m m" },
hyphenation: { signature: "m" },
footnote: { signature: "o m", renderInfo: { inParMode: true } },
footnotemark: { signature: "o" },
footnotetext: { signature: "o m", renderInfo: { inParMode: true } },
Expand All @@ -194,20 +194,11 @@ export const macros: MacroInfoRecord = {
maketitle: { renderInfo: { breakAround: true } },
doublespacing: { renderInfo: { breakAround: true } },
singlespacing: { renderInfo: { breakAround: true } },
author: {
signature: "m",
renderInfo: { breakAround: true, inParMode: true },
},
date: { signature: "o m", renderInfo: { breakAround: true } },
thanks: {
signature: "m",
renderInfo: { breakAround: true, inParMode: true },
},
// amsart document class adds an optional argument
title: {
signature: "o m",
renderInfo: { breakAround: true, inParMode: true },
},
pagenumbering: { signature: "m", renderInfo: { breakAround: true } },
pagestyle: { signature: "m", renderInfo: { breakAround: true } },
thispagestyle: { signature: "m", renderInfo: { breakAround: true } },
Expand Down
88 changes: 88 additions & 0 deletions packages/unified-latex-to-pretext/libs/author-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as Ast from "@unified-latex/unified-latex-types";
import { visit } from "@unified-latex/unified-latex-util-visit";
import { match } from "@unified-latex/unified-latex-util-match";
import { htmlLike } from "@unified-latex/unified-latex-util-html-like";
import { VFileMessage } from "vfile-message";
import { VFile } from "vfile";

export type AuthorInfo = Record<string, Ast.Node[]>;

/**
* Visits all the matching nodes and gathers author information, then send them to render and output pretext.
*/
export function gatherAuthorInfo(ast: Ast.Ast, file: VFile): AuthorInfo[] {
const authorList: AuthorInfo[] = [];

visit(ast, (node) => {
if (match.macro(node, "author") && node.args) {
const authorName = Object.fromEntries(
node.args.map((x) => ["personname", x.content])
);
authorList.push(authorName);
} else if (match.macro(node, "address") && node.args) {
const authorAdd = Object.fromEntries(
node.args.map((x) => ["institution", x.content])
);
authorList.push(authorAdd);
} else if (match.macro(node, "email") && node.args) {
const authorEmail = Object.fromEntries(
node.args.map((x) => ["email", x.content])
);
authorList.push(authorEmail);
} else if (match.macro(node, "affil")) {
const message = createVFileMessage(node);
file.message(
message,
message.position,
"latex-to-pretext:warning"
)
}
});
return authorList;
}

/**
* This function is called after the author information is collected, and integrate them into one htmlLike node with "author" tag.
*/
export function renderCollectedAuthorInfo(authorList: AuthorInfo[]): Ast.Macro {
let authorInfo: Ast.Macro[] = [];
for (const info of authorList) {
for (const key in info) {
const renderInfo = htmlLike({
tag: key,
content: info[key],
});
authorInfo.push(renderInfo);
}
}
const renderedAuthorList = htmlLike({
tag: "author",
content: authorInfo,
});
return renderedAuthorList;
}

function createVFileMessage(node: Ast.Macro): VFileMessage {
const message = new VFileMessage(
`Macro \"${node.content}\" is not supported`
);

// add the position of the macro if available
if (node.position) {
message.line = node.position.start.line;
message.column = node.position.start.column;
message.position = {
start: {
line: node.position.start.line,
column: node.position.start.column,
},
end: {
line: node.position.end.line,
column: node.position.end.column,
},
};
}

message.source = "latex-to-pretext:warning";
return message;
}
111 changes: 111 additions & 0 deletions packages/unified-latex-to-pretext/tests/author-info.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { describe, it, expect } from "vitest";
import Prettier from "prettier";
import util from "util";
import { getParser } from "@unified-latex/unified-latex-util-parse";
import { toXml } from "xast-util-to-xml";
import { xmlCompilePlugin } from "../libs/convert-to-pretext";
import { unified } from "unified";
import {
gatherAuthorInfo,
renderCollectedAuthorInfo,
} from "../libs/author-info";
import { VFile } from "vfile";
import { toPretextWithLoggerFactory } from "../libs/pretext-subs/to-pretext";

function normalizeHtml(str: string) {
try {
return Prettier.format(str, { parser: "html" });
} catch {
console.warn("Could not format HTML string", str);
return str;
}
}
/* eslint-env jest */

// Make console.log pretty-print by default
const origLog = console.log;
console.log = (...args) => {
origLog(...args.map((x) => util.inspect(x, false, 10, true)));
};

describe("unified-latex-to-pretext:author-info", () => {
let sample: string;
const parser = getParser();
let file: VFile;

it("collects author name, address, institution, and email information", () => {
file = new VFile();
sample =
"\\author{First Middle LastName} \n \\address{Department, Address}";
let input = " First Middle LastName";
let input1 =
" \n Department, Address";
expect(gatherAuthorInfo(parser.parse(sample), file)).toEqual([
{ personname: parser.parse(input).content },
{ institution: parser.parse(input1).content },
]);

sample = "\\address{Affiliation}";
input = " Affiliation";
expect(gatherAuthorInfo(parser.parse(sample), file)).toEqual([
{ institution: parser.parse(input).content },
]);

sample = "\\affil{Affiliation}";
expect(gatherAuthorInfo(parser.parse(sample), file)).toEqual([]);

sample =
"\\author{First Author} \\email{example@example.com} \\author{Second Author}";
input = " First Author";
input1 = " example@example.com";
let input2 =
" Second Author";
expect(gatherAuthorInfo(parser.parse(sample), file)).toEqual([
{ personname: parser.parse(input).content },
{ email: parser.parse(input1).content },
{ personname: parser.parse(input2).content },
]);
});

it("parses author name, address, and email information", () => {
sample =
"\\author{First Middle LastName} \n \\address{Department, Address}";
let rendered = renderCollectedAuthorInfo(
gatherAuthorInfo(parser.parse(sample), file)
);
const toXast = toPretextWithLoggerFactory(file.message.bind(file));
const xxx = unified()
.use(xmlCompilePlugin)
.runSync({ type: "root", children: [toXast(rendered)].flat() });
expect(normalizeHtml(toXml(xxx))).toEqual(
normalizeHtml(
"<author><personname>First Middle LastName</personname><institution>Department, Address</institution></author>"
)
);

sample = "\\address{Affiliation}";
rendered = renderCollectedAuthorInfo(
gatherAuthorInfo(parser.parse(sample), file)
);
const xxx1 = unified()
.use(xmlCompilePlugin)
.runSync({ type: "root", children: [toXast(rendered)].flat() });
expect(normalizeHtml(toXml(xxx1))).toEqual(
normalizeHtml("<author><institution>Affiliation</institution></author>")
);

sample =
"\\author{First Author} \\email{example@example.com} \\author{Second Author}";
rendered = renderCollectedAuthorInfo(
gatherAuthorInfo(parser.parse(sample), file)
);
const xxx2 = unified()
.use(xmlCompilePlugin)
.runSync({ type: "root", children: [toXast(rendered)].flat() });
expect(normalizeHtml(toXml(xxx2))).toEqual(
normalizeHtml(
"<author><personname>First Author</personname><email>example@example.com</email><personname>Second Author</personname></author>"
)
);
});
});
6 changes: 6 additions & 0 deletions packages/unified-latex-util-ligatures/libs/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export function createMatchers() {
if (match.group(arg) && arg.content.length === 1) {
nodesToTest[1] = arg.content[0];
}

// whitespace directly after one-letter macros like `\o` should
// be skipped
if (nodes[0].content.length === 1 && nodesToTest[1].type === 'whitespace') {
nodesToTest.length = 1
}
}
return ligatureToUnicode(nodesToTest);
},
Expand Down
3 changes: 3 additions & 0 deletions packages/unified-latex-util-ligatures/tests/ligatures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ describe("unified-latex-util-ligatures", () => {

ast = strToNodes("a\\v sb");
expect(printRaw(parseLigatures(ast))).toEqual("ašb");

ast = strToNodes("r\\o y");
expect(printRaw(parseLigatures(ast))).toEqual("røy");
});

it("can replace macro ligatures with an argument in a group", () => {
Expand Down

0 comments on commit e09f4cc

Please sign in to comment.