Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expanding default arguments support #84

Merged
merged 14 commits into from
Feb 20, 2024
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
Loading