Skip to content
Merged
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
3 changes: 3 additions & 0 deletions tools/adventure-pack/goodies/java/src/pair/Pair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package pair;

public record Pair<TFirst, TSecond>(TFirst first, TSecond second) {}
3 changes: 3 additions & 0 deletions tools/adventure-pack/goodies/java/src/pair/goody.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "Pair"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App can equip single goody: Java Pair 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
// Running at: https://example.com/

record Pair<TFirst, TSecond>(TFirst first, TSecond second) {}

/////////////////////////// END ADVENTURE PACK CODE ////////////////////////////"
`;

exports[`App can equip single goody: Java UnionFind 1`] = `
"////////////////////////// BEGIN ADVENTURE PACK CODE ///////////////////////////
// Adventure Pack commit fake-commit-hash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App can render goody: Java Pair 1`] = `
"package pair;

record Pair<TFirst, TSecond>(TFirst first, TSecond second) {}"
`;

exports[`App can render goody: Java UnionFind 1`] = `
"package union_find;

Expand Down
4 changes: 2 additions & 2 deletions tools/adventure-pack/src/app/__tests__/render-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, expect, it } from "@jest/globals";
import { LANGUAGE_NAMES } from "../constants";
import type { Language } from "../Language";
import { readAllGoodies } from "../../scripts/package-goodies/readAllGoodies";
import { goodyToText } from "../goodyToText";
import { stringifyGoody } from "../stringifyGoody";

describe("App", () => {
it("can render goody", async () => {
Expand All @@ -12,7 +12,7 @@ describe("App", () => {
for (const language of Object.keys(goodiesByLanguage) as Language[]) {
const goodies = goodiesByLanguage[language];
for (const goody of Object.values(goodies)) {
const code = goodyToText(goody);
const code = stringifyGoody(goody);

expect(code).toMatchSnapshot(
`${LANGUAGE_NAMES[language]} ${goody.name}`,
Expand Down
4 changes: 2 additions & 2 deletions tools/adventure-pack/src/app/components/GoodyCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";

import type { Goody } from "../Goody";
import { goodyToText } from "../goodyToText";
import { stringifyGoody } from "../stringifyGoody";
import { HighlightedCode } from "./HighlightedCode";

type Props = {
Expand All @@ -13,7 +13,7 @@ export function GoodyCard({ goody }: Props) {
<div>
<h2>{goody.name}</h2>
<HighlightedCode language={goody.language}>
{goodyToText(goody)}
{stringifyGoody(goody)}
</HighlightedCode>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions tools/adventure-pack/src/app/mergeCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ export function mergeCode({
for (const [moduleName, interfaceDeclarations] of Object.entries(
goody.moduleDeclarations,
)) {
for (const [interfaceName, codeGroups] of Object.entries(
for (const [interfaceName, codeSections] of Object.entries(
interfaceDeclarations,
)) {
((mergedDeclarations[moduleName] ??= {})[interfaceName] ??=
[]).push(...codeGroups);
[]).push(...codeSections);
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions tools/adventure-pack/src/app/mergeJavaCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ import type { JavaGoody } from "./parsers/javaGoodyParser";
const ADVENTURE_PACK_CLASS_NAME = "AP";

export function mergeJavaCode(goodies: Iterable<ReadonlyDeep<JavaGoody>>) {
const classes: Record<string, { code: string[]; modifiers: Set<string> }> =
{};
const classes: Record<string, { code: string[]; declaration: string }> = {};
for (const goody of goodies) {
for (const className of Object.keys(goody.codeByClass)) {
invariant(
classes[className] == null || className === ADVENTURE_PACK_CLASS_NAME,
`Only the ${ADVENTURE_PACK_CLASS_NAME} class can exist in multiple goodies!`,
);

classes[className] ??= { code: [], modifiers: new Set() };
for (const modifier of goody.codeByClass[className].modifiers) {
classes[className].modifiers.add(modifier);
}
classes[className] ??= {
code: [],
declaration: goody.codeByClass[className].declaration,
};
classes[className].code.push(goody.codeByClass[className].code);
}
}
Expand All @@ -32,8 +31,13 @@ export function mergeJavaCode(goodies: Iterable<ReadonlyDeep<JavaGoody>>) {

const res: string[] = [];
for (const className of Object.keys(classes)) {
const classData = classes[className];
const codeSections = classData.code.filter(Boolean);

res.push(
`${[...classes[className].modifiers, "class", className].join(" ")} {\n${classes[className].code.map((section) => section + "\n").join("\n")}}`,
codeSections.length > 0
? `${classData.declaration}\n${codeSections.map((codeSection) => codeSection + "\n").join("\n")}}`
: classData.declaration + "}",
);
}

Expand Down
2 changes: 1 addition & 1 deletion tools/adventure-pack/src/app/parsers/javaGoodyParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const javaGoodyParser = goodyBaseParser
z
.object({
code: z.string(),
modifiers: z.array(nonBlankStringParser),
declaration: nonBlankStringParser,
})
.strict(),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function sortTypeScriptModuleAndInterfaceDeclarations(
): Record<string, Record<string, string[]>> {
return sortObjectKeysRecursive(
mapObjectValues(moduleDeclarations, (interfaceDeclarations) =>
mapObjectValues(interfaceDeclarations, (codeGroups) =>
[...codeGroups].sort(compareStringsCaseInsensitive),
mapObjectValues(interfaceDeclarations, (codeSections) =>
[...codeSections].sort(compareStringsCaseInsensitive),
),
),
compareStringsCaseInsensitive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Goody } from "./Goody";
import { mergeJavaCode } from "./mergeJavaCode";
import { stringifyTypeScriptModuleDeclarations } from "./stringifyTypeScriptModuleDeclarations";

export function goodyToText(goody: Goody): string {
export function stringifyGoody(goody: Goody): string {
switch (goody.language) {
case "java": {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import type { ReadonlyDeep } from "type-fest";

export function stringifyTypeScriptInterfaceDeclarations(
interfaceDeclarations: ReadonlyDeep<Record<string, string[]>>,
indent: string = "",
{ indent = "" }: { indent?: string } = {},
): string {
return Object.entries(interfaceDeclarations)
.map(
([interfaceName, codeGroups]) =>
`${indent}interface ${interfaceName} {\n${codeGroups.join("\n\n")}\n${indent}}`,
([interfaceName, codeSections]) =>
`${indent}interface ${interfaceName} {\n${codeSections.join("\n\n")}\n${indent}}`,
)
.join("\n\n");
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function stringifyTypeScriptModuleDeclarations(
return Object.entries(moduleDeclarations)
.map(
([moduleName, interfaceDeclarations]) =>
`declare ${moduleName} {\n${stringifyTypeScriptInterfaceDeclarations(interfaceDeclarations, " ")}\n}`,
`declare ${moduleName} {\n${stringifyTypeScriptInterfaceDeclarations(interfaceDeclarations, { indent: " " })}\n}`,
)
.join("\n\n");
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import invariant from "invariant";

import {
compareStringsCaseInsensitive,
getLines,
isStringEmptyOrWhitespaceOnly,
mapObjectValues,
} from "@code-chronicles/util";

export function splitCodeIntoClasses(
code: string,
): Record<string, { code: string; modifiers: string[] }> {
const classes: Record<string, { code: string[]; modifiers: Set<string> }> =
{};
): Record<string, { code: string; declaration: string }> {
const classes: Record<string, { code: string[]; declaration: string }> = {};

let currentClassName: string | null = null;
for (const line of getLines(code)) {
const classMatch = line.match(
/^((?:(?:abstract|final|public)\s+)*)class\s+(\S+)\s*{/,
/^((?:(?:abstract|final|public)\s+)*)((?:class|enum|interface|record)\s+(\S+)\s*[{<].*)$/s,
);
if (classMatch != null) {
invariant(currentClassName == null, "Top-level class nesting?");

const modifiers = new Set(classMatch[1].trim().split(/\s+/));
modifiers.delete("public");

currentClassName = classMatch[2];
classes[currentClassName] = { code: [], modifiers };
currentClassName = classMatch[3];
classes[currentClassName] = {
code: [],
declaration: [
...Array.from(modifiers).sort(compareStringsCaseInsensitive),
classMatch[2].replace(/}?\n*$/, ""),
]
.filter(Boolean)
.join(" "),
};

if (/}\n*$/.test(line)) {
currentClassName = null;
}

continue;
}

Expand All @@ -44,8 +57,8 @@ export function splitCodeIntoClasses(

invariant(currentClassName == null, "Unfinished class?");

return mapObjectValues(classes, ({ code, modifiers }) => ({
return mapObjectValues(classes, ({ code, declaration }) => ({
code: code.join("").replace(/^\n+/, "").replace(/\n+$/, ""),
modifiers: Array.from(modifiers),
declaration,
}));
}