Skip to content

Commit

Permalink
Expanding default arguments support (#84)
Browse files Browse the repository at this point in the history
* Expand default arguments with references to other arguments

Test's courtesy of @theseanl
  • Loading branch information
siefkenj committed Feb 20, 2024
1 parent 898ac25 commit 818c4dd
Show file tree
Hide file tree
Showing 9 changed files with 585 additions and 33 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# unified-latex Changelog

### v1.7.0
- Switch build system to `vite`. Should result in smaller bundles.
- Save default arguments when parsing if the macro signature specifies them e.g. `{signature: "O{foo}"}`. The defaults are substituted in when expanding the macros with the optional arguments omitted.

### v1.6.1
- Pass `VisitInfo` as an additional argument ot `macroReplacers` and `environmentReplacers` in `unifiedLatexToHast`.
- Allow skipping of HTML validation in `unifiedLatexToHast`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ hi $\\\\x fooArg$ and baz!
"
`;
exports[`unified-latex-cli > can expand macro 3`] = `
"% Expand via> tests/needs-expanding.tex --stats -e \\"\\\\\\\\newcommand{foo}[1]{FOO(#1)}\\" -e '{name: \\"bar\\", body: \\"baz\\"}'
hi FOO(fooArg) and baz!
"
`;
exports[`unified-latex-cli > can expand macros defined in document 1`] = `
"\\\\newcommand{\\\\foo}[1]{$BAR #1$}
\\\\DeclareDocumentCommand{\\\\baz}{m}{\\\\foo{xxx} .#1.}
Expand Down
10 changes: 10 additions & 0 deletions packages/unified-latex-cli/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ describe(
]);
expect(stdout).toMatchSnapshot();
}
{
let stdout = await execCLI([
`${examplesPath}/needs-expanding.tex`,
`-e`,
"\\newcommand{foo}[2][FOO]{#1(#2)}",
`-e`,
'{name: "bar", signature: "O{baz}", body: "#1"}',
]);
expect(stdout).toMatchSnapshot();
}
});
it("can expand macros defined in document", async () => {
const stdout = await execCLI([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { describe, expect, it } from "vitest";
import util from "util";
import type * as Ast from "@unified-latex/unified-latex-types";
import { attachMacroArgs } from "../libs/attach-arguments";
import { strToNodes } from "../../test-common";
import { arg, s, SP } from "@unified-latex/unified-latex-builder";

/* 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-util-arguments", () => {
it("Default arguments are stored if provided and no argument given", () => {
let nodes = strToNodes(`\\xxx`);
attachMacroArgs(nodes, {
xxx: {
signature: "O{x}",
},
});
expect(nodes).toEqual([
{
type: "macro",
content: "xxx",
args: [
{
type: "argument",
content: [],
openMark: "",
closeMark: "",
_renderInfo: { defaultArg: "x" },
},
],
},
]);

nodes = strToNodes(`\\xxx`);
attachMacroArgs(nodes, {
xxx: {
signature: "D(){x}",
},
});
expect(nodes).toEqual([
{
type: "macro",
content: "xxx",
args: [
{
type: "argument",
content: [],
openMark: "",
closeMark: "",
_renderInfo: { defaultArg: "x" },
},
],
},
]);

nodes = strToNodes(`\\xxx`);
attachMacroArgs(nodes, {
xxx: {
signature: "E{_}{x}",
},
});
expect(nodes).toEqual([
{
type: "macro",
content: "xxx",
args: [
{
type: "argument",
content: [],
openMark: "",
closeMark: "",
_renderInfo: { defaultArg: "x" },
},
],
},
]);
});
it("Embellishment arguments keep track of their defaults", () => {
let nodes = strToNodes(`\\xxx_{a}`);
attachMacroArgs(nodes, {
xxx: {
signature: "E{_^}{xy}",
},
});
expect(nodes).toEqual([
{
args: [
{
closeMark: "",
content: [
{
content: "a",
type: "string",
},
],
openMark: "_",
type: "argument",
},
{
_renderInfo: {
defaultArg: "y",
},
closeMark: "",
content: [],
openMark: "",
type: "argument",
},
],
content: "xxx",
type: "macro",
},
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { VFile } from "unified-lint-rule/lib";
import util from "util";
import { trimRenderInfo } from "../../unified-latex-util-render-info";
import * as Ast from "@unified-latex/unified-latex-types";
import * as Ast from "@unified-latex/unified-latex-types/index";
import { parse as parseArgspec } from "@unified-latex/unified-latex-util-argspec";
import { gobbleArguments } from "../libs/gobble-arguments";
import { processLatexToAstViaUnified } from "@unified-latex/unified-latex";
Expand Down Expand Up @@ -239,4 +239,53 @@ describe("unified-latex-util-arguments", () => {
});
expect(nodes).toEqual([s("x"), SP, s("x")]);
});
it("can gobble arguments that represents multiple embellishments with default arguments", () => {
let argspec = parseArgspec("E{^_}{{UP}{DOWN}}");

value = "^{SuperscriptOnly}";
file = processLatexToAstViaUnified().processSync({ value });
let nodes = trimRenderInfo((file.result as any).content) as Ast.Node[];
expect(gobbleArguments(nodes, argspec)).toEqual({
args: [
{
type: "argument",
content: [{ type: "string", content: "SuperscriptOnly" }],
openMark: "^",
closeMark: "",
},
{
_renderInfo: { defaultArg: "DOWN" },
type: "argument",
content: [],
openMark: "",
closeMark: "",
},
],
nodesRemoved: 2,
});
expect(nodes).toEqual([]);

value = "_{SubscriptOnly}";
file = processLatexToAstViaUnified().processSync({ value });
nodes = trimRenderInfo((file.result as any).content) as Ast.Node[];
expect(gobbleArguments(nodes, argspec)).toEqual({
args: [
{
_renderInfo: { defaultArg: "UP" },
type: "argument",
content: [],
openMark: "",
closeMark: "",
},
{
type: "argument",
content: [{ type: "string", content: "SubscriptOnly" }],
openMark: "_",
closeMark: "",
},
],
nodesRemoved: 2,
});
expect(nodes).toEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { VFile } from "unified-lint-rule/lib";
import util from "util";
import { trimRenderInfo } from "../../unified-latex-util-render-info";
import * as Ast from "@unified-latex/unified-latex-types";
import type * as Ast from "../../unified-latex-types/index";
import { parse as parseArgspec } from "@unified-latex/unified-latex-util-argspec";
import { gobbleSingleArgument } from "../libs/gobble-single-argument";
import { processLatexToAstViaUnified } from "@unified-latex/unified-latex";
Expand Down Expand Up @@ -749,4 +749,105 @@ describe("unified-latex-util-arguments", () => {
nodesRemoved: 2,
});
});
it("can skip optional argument with default argument", () => {
const expectNoMatch = (ast: Ast.Node[]) => {
expect(
gobbleSingleArgument(ast, parseArgspec("O{default}")[0])
).toMatchObject({
argument: null,
nodesRemoved: 0,
});

expect(
gobbleSingleArgument(ast, parseArgspec("D(){\\LaTeX}")[0])
).toMatchObject({
argument: null,
nodesRemoved: 0,
});

expect(
gobbleSingleArgument(ast, parseArgspec("R^_{default}")[0])
).toMatchObject({
argument: null,
nodesRemoved: 0,
});
};

expectNoMatch([{ type: "string", content: "this_should_not_match" }]);
expectNoMatch([{ type: "whitespace" }, { type: "parbreak" }]);
expectNoMatch([]);
});
it.skip("gobbleSingleArgument gobbles arguments delimited by tokens", () => {
let ast: Ast.Node[] = [
{ type: "macro", content: "a" },
{ type: "group", content: [{ type: "string", content: "123" }] },
{ type: "string", content: "1" },
];
expect(
gobbleSingleArgument(ast, parseArgspec("r\\a{ 1 }")[0])
).toMatchObject({
argument: {
type: "argument",
content: [
{
type: "group",
content: [{ type: "string", content: "123" }],
},
],
openMark: "\\a",
closeMark: "1",
},
nodesRemoved: 3,
});

ast = [
{ type: "macro", content: "abc" },
{ type: "string", content: "123" },
{ type: "macro", content: "def" },
];
expect(
gobbleSingleArgument(ast, parseArgspec("r\\abc\\def")[0])
).toMatchObject({
argument: {
type: "argument",
content: [{ type: "string", content: "123" }],
openMark: "\\abc",
closeMark: "\\def",
},
nodesRemoved: 3,
});
});
// XXX: Test copied from PR #62. Output should be updated when `until` arguments
// are more properly implemented.
it.skip("can gobble an 'until' argument with multiple stop tokens", () => {
let argspec = parseArgspec("u{| \\stop}")[0];
value = "|ThisBarIsNotAStop|{Token}This| \\stop Is.";
file = processLatexToAstViaUnified().processSync({ value });
let nodes = trimRenderInfo((file.result as any).content) as Ast.Node[];
expect(gobbleSingleArgument(nodes, argspec)).toEqual({
argument: {
type: "argument",
content: [
// Due to a current implementation of gobbleSingleArgument,
// we may introduce extra string split during the search.
{ type: "string", content: "|" },
{ type: "string", content: "ThisBarIsNotAStop" },
{ type: "string", content: "|" },
{
type: "group",
content: [{ type: "string", content: "Token" }],
},
{ type: "string", content: "This" },
],
openMark: "",
closeMark: "| \\stop",
},
nodesRemoved: 8,
});
expect(nodes).toEqual([
{ type: "whitespace" },
{ type: "string", content: "Is" },
{ type: "string", content: "." },
]);
});
});
Loading

0 comments on commit 818c4dd

Please sign in to comment.