Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lua-doc-extractor",
"version": "3.3.3",
"version": "3.4.0",
"description": "Extracts lua documentation from C-style comments",
"main": "dist/src/index.js",
"homepage": "https://github.com/rhys-vdw/lua-doc-extractor",
Expand Down
37 changes: 33 additions & 4 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { addHeader, formatDocs, getDocs, processDocs } from ".";
import project from "../package.json";
import { Doc } from "./doc";
import { toResultAsync } from "./result";
import { parseTableMappings } from "./tableMapping";

interface Options {
src: string[];
Expand All @@ -21,6 +22,8 @@ interface Options {
error?: boolean;
repo?: string;
file?: string;
"table-mapping"?: string[];
"strip-helpers"?: boolean;
}
const optionList = [
{
Expand Down Expand Up @@ -71,6 +74,22 @@ const optionList = [

`,
},
{
name: "table-mapping",
type: String,
multiple: true,
defaultValue: [],
typeLabel: "{underline from:to} ...",
description:
'Remap table names in the output. Format: "OldName:NewName". Can be specified multiple times.\n',
},
{
name: "strip-helpers",
type: Boolean,
defaultValue: false,
description:
"{white (Default: false)} Strip standalone helper types (classes, enums, aliases) from the output. Keeps only function and table declarations.\n",
},
{
name: "error",
type: Boolean,
Expand Down Expand Up @@ -135,6 +154,8 @@ async function runAsync() {
repo,
file,
error: enableErrorCode,
"table-mapping": tableMappingRaw,
"strip-helpers": stripHelpers,
} = options;

if (version) {
Expand All @@ -147,6 +168,8 @@ async function runAsync() {
process.exit(0);
}

const tableMapping = parseTableMappings(tableMappingRaw ?? []);

const srcFiles = await glob(src);

if (srcFiles.length === 0) {
Expand Down Expand Up @@ -203,7 +226,7 @@ async function runAsync() {
const rel = relative(cwd(), path);
const outPath = join(dest, `${rel}.lua`);
if (ds.length > 0) {
await writeLibraryFile(ds, outPath, repo, [path]);
await writeLibraryFile(ds, outPath, repo, [path], tableMapping, stripHelpers);
}
})
);
Expand All @@ -215,7 +238,9 @@ async function runAsync() {
valid.flatMap(([, ds]) => ds),
outPath,
repo,
sources
sources,
tableMapping,
stripHelpers
);
}

Expand All @@ -233,10 +258,14 @@ async function writeLibraryFile(
docs: Doc[],
outPath: string,
repo?: string,
sources: string[] = []
sources: string[] = [],
tableMapping?: ReadonlyMap<string, string>,
stripHelpers?: boolean
) {
try {
const formattedDocs = formatDocs(processDocs(docs, repo ?? null));
const formattedDocs = formatDocs(
processDocs(docs, repo ?? null, { tableMapping, stripHelpers })
);
await mkdir(dirname(outPath), { recursive: true });
await writeFile(outPath, addHeader(formattedDocs, sources));
console.log(chalk`{bold.blue ►} '{white ${outPath}}'`);
Expand Down
17 changes: 16 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { header } from "./header";
import { fail, Result, success } from "./result";
import { applyRules } from "./rules";
import { appendSourceLinks } from "./source";
import { stripHelperTypes } from "./stripHelpers";
import { applyTableMapping } from "./tableMapping";
import { addTables, mergeTables } from "./tables";
import { trimTrailingWhitespace } from "./utility";

Expand Down Expand Up @@ -41,10 +43,23 @@ function runProcessors(docs: Doc[], processors: readonly DocProcessor[]) {
return processors.reduce((acc, processor) => processor(acc), docs);
}

export function processDocs(docs: Doc[], repoUrl: string | null): Doc[] {
export interface ProcessDocsOptions {
tableMapping?: ReadonlyMap<string, string> | null;
stripHelpers?: boolean;
}

export function processDocs(
docs: Doc[],
repoUrl: string | null,
options?: ProcessDocsOptions
): Doc[] {
const opts = options ?? {};

return runProcessors(docs, [
removeEmptyDocs,
appendSourceLinks(repoUrl),
applyTableMapping(opts.tableMapping ?? null),
...(opts.stripHelpers ? [stripHelperTypes] : []),
processGlobals,
addTables,
addTableToEnumFields,
Expand Down
15 changes: 15 additions & 0 deletions src/stripHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { isAttribute } from "./attribute";
import { Doc } from "./doc";

/**
* Strip docs that only define helper types (classes, enums, aliases).
* Keep docs that define functions or tables -- these are the table-scoped
* API members that benefit from --table-mapping.
*/
export function stripHelperTypes(docs: Doc[]): Doc[] {
return docs.filter((doc) =>
doc.attributes.some(
(a) => isAttribute(a, "function") || isAttribute(a, "table")
)
);
}
72 changes: 72 additions & 0 deletions src/tableMapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Attribute, isAttribute } from "./attribute";
import { Doc } from "./doc";

function mapName(
name: readonly string[],
mapping: ReadonlyMap<string, string>
): readonly string[] {
if (name.length === 0) return name;
const mapped = mapping.get(name[0]);
if (mapped == null) return name;
return [mapped, ...name.slice(1)];
}

export const applyTableMapping =
(mapping: ReadonlyMap<string, string> | null) =>
(docs: Doc[]): Doc[] => {
if (mapping == null || mapping.size === 0) return docs;

docs.forEach((doc) => {
doc.attributes = doc.attributes.map((attr) => {
if (isAttribute(attr, "function")) {
return {
...attr,
args: { ...attr.args, name: mapName(attr.args.name, mapping) },
};
}
if (isAttribute(attr, "table")) {
return {
...attr,
args: { ...attr.args, name: mapName(attr.args.name, mapping) },
};
}
if (isAttribute(attr, "field")) {
return {
...attr,
args: { ...attr.args, name: mapName(attr.args.name, mapping) },
};
}
if (isAttribute(attr, "enum")) {
return {
...attr,
args: { ...attr.args, name: mapName(attr.args.name, mapping) },
};
}
if (isAttribute(attr, "global")) {
return {
...attr,
args: { ...attr.args, name: mapName(attr.args.name, mapping) },
};
}
return attr;
});
});

return docs;
};

export function parseTableMappings(
raw: readonly string[]
): Map<string, string> {
const mapping = new Map<string, string>();
for (const entry of raw) {
const colon = entry.indexOf(":");
if (colon === -1) {
throw new Error(
`Invalid table-mapping "${entry}". Expected format: "OldName:NewName"`
);
}
mapping.set(entry.slice(0, colon), entry.slice(colon + 1));
}
return mapping;
}
125 changes: 125 additions & 0 deletions src/test/stripHelpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import dedent from "dedent-js";
import { testInput } from "./utility/harness";

const opts = { stripHelpers: true };

testInput(
"stripHelpers: keeps function declarations",
dedent`
/***
* Get the current game frame.
*
* @function Spring.GetGameFrame
* @return integer frame
*/
`,
dedent`
---Get the current game frame.
---
---@return integer frame
function Spring.GetGameFrame() end
`,
undefined,
opts
);

testInput(
"stripHelpers: keeps table declarations",
dedent`
/*** @table Spring.MoveCtrl */
`,
dedent`
Spring.MoveCtrl = {}
`,
undefined,
opts
);

testInput(
"stripHelpers: strips standalone class",
dedent`
/***
* @class losAccess
* @x_helper
* @field public private boolean? only readable by the ally (default)
* @field public allied boolean? readable by ally + ingame allied
*/
`,
``,
undefined,
opts
);

testInput(
"stripHelpers: strips standalone enum",
dedent`
/***
* @enum LosMask
* @field LOS_INLOS integer
* @field LOS_INRADAR integer
*/
`,
``,
undefined,
opts
);

testInput(
"stripHelpers: strips standalone alias",
dedent`
/***
* @alias Heading integer
*/
`,
``,
undefined,
opts
);

testInput(
"stripHelpers: keeps function, strips class from same input",
dedent`
/***
* @class losAccess
* @x_helper
* @field public private boolean? only readable by the ally (default)
*/

/***
* Set game rules param.
*
* @function Spring.SetGameRulesParam
* @param name string
*/
`,
dedent`
---Set game rules param.
---
---@param name string
function Spring.SetGameRulesParam(name) end
`,
undefined,
opts
);

testInput(
"stripHelpers: combines with table-mapping",
dedent`
/***
* @class losAccess
* @x_helper
* @field public private boolean? only readable by the ally (default)
*/

/***
* @function Spring.SetGameRulesParam
* @param name string
*/
`,
dedent`
---@param name string
function SpringSynced.SetGameRulesParam(name) end
`,
undefined,
{ ...opts, tableMapping: new Map([["Spring", "SpringSynced"]]) }
);
Loading