Skip to content

Commit

Permalink
[WIP] author-info (#100)
Browse files Browse the repository at this point in the history
Add function to extract information from the `\author{...}` tag that appears before `\begin{document}...`
  • Loading branch information
Bo-Y-G authored Aug 7, 2024
1 parent dc71b65 commit 419b8df
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
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{[email protected]} \\author{Second Author}";
input = " First Author";
input1 = " [email protected]";
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{[email protected]} \\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>[email protected]</email><personname>Second Author</personname></author>"
)
);
});
});

0 comments on commit 419b8df

Please sign in to comment.