diff --git a/cli-console/src/main/java/org/aya/cli/console/Main.java b/cli-console/src/main/java/org/aya/cli/console/Main.java index 91907672d4..b5f92d5120 100644 --- a/cli-console/src/main/java/org/aya/cli/console/Main.java +++ b/cli-console/src/main/java/org/aya/cli/console/Main.java @@ -106,25 +106,23 @@ private int doCompile(@NotNull CompileAction compile) throws IOException { return compiler.compile(filePath, null); } - private @Nullable CompilerFlags.PrettyInfo - computePrettyInfo( + private @Nullable CompilerFlags.PrettyInfo computePrettyInfo( @Nullable Path outputPath, RenderOptions renderOptions, AyaPrettierOptions prettierOptions ) { - return prettyStage == null - ? (outputPath != null ? CompilerFlags.prettyInfoFromOutput( - outputPath, renderOptions, prettyNoCodeStyle, prettyInlineCodeStyle, prettySSR) : null) - : new CompilerFlags.PrettyInfo( - asciiOnly, - prettyNoCodeStyle, - prettyInlineCodeStyle, - prettySSR, - prettyStage, - prettyFormat, - prettierOptions, - renderOptions, - prettyDir - ); + if (prettyStage == null) + return outputPath != null ? CompilerFlags.prettyInfoFromOutput( + outputPath, renderOptions, prettyNoCodeStyle, prettyInlineCodeStyle, prettySSR) : null; + return new CompilerFlags.PrettyInfo( + asciiOnly, + prettyNoCodeStyle, + prettyInlineCodeStyle, + prettySSR, + prettyStage, + prettyFormat, + prettierOptions, renderOptions, + datetimeFrontMatterKey, datetimeFrontMatterValue, prettyDir + ); } private @NotNull RenderOptions createRenderOptions(@NotNull ReplConfig replConfig) { @@ -132,7 +130,7 @@ private int doCompile(@NotNull CompileAction compile) throws IOException { switch (prettyColor) { case emacs -> renderOptions.colorScheme = RenderOptions.ColorSchemeName.Emacs; case intellij -> renderOptions.colorScheme = RenderOptions.ColorSchemeName.IntelliJ; - case null -> {} + case null -> { } } return renderOptions; } diff --git a/cli-console/src/main/java/org/aya/cli/console/MainArgs.java b/cli-console/src/main/java/org/aya/cli/console/MainArgs.java index 08655ef887..f25c445ffc 100644 --- a/cli-console/src/main/java/org/aya/cli/console/MainArgs.java +++ b/cli-console/src/main/java/org/aya/cli/console/MainArgs.java @@ -81,6 +81,10 @@ public static class Action { public PrettyFormat prettyFormat; @Option(names = {"--pretty-dir"}, description = "Specify output directory of pretty printing.") public String prettyDir; + @Option(names = {"--datetime-front-matter-key"}, description = "If set, add datetime in the front matter using the value as YAML key.") + public String datetimeFrontMatterKey; + @Option(names = {"--datetime-front-matter"}, description = "Overwrites the datetime in the front matter.") + public String datetimeFrontMatterValue; @Option(names = {"--pretty-color"}, description = "The color theme of pretty printing." + CANDIDATES, defaultValue = "emacs") public PredefinedStyle prettyColor; @Option(names = {"--pretty-no-code-style"}, description = "Do not include default highlight styles.") diff --git a/cli-impl/src/main/java/org/aya/cli/library/LibraryCompiler.java b/cli-impl/src/main/java/org/aya/cli/library/LibraryCompiler.java index d092eb068e..71b311326d 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/LibraryCompiler.java +++ b/cli-impl/src/main/java/org/aya/cli/library/LibraryCompiler.java @@ -13,6 +13,7 @@ import org.aya.cli.single.CompilerFlags; import org.aya.cli.utils.CliEnums; import org.aya.cli.utils.CompilerUtil; +import org.aya.cli.utils.LiterateData; import org.aya.generic.InterruptException; import org.aya.pretty.backend.string.StringPrinterConfig; import org.aya.pretty.printer.PrinterConfig; @@ -171,6 +172,12 @@ private void pretty(ImmutableSeq modified) throws IOException { var litPretty = litConfig.pretty(); var prettierOptions = litPretty != null ? litPretty.prettierOptions : cmdPretty.prettierOptions(); var renderOptions = litPretty != null ? litPretty.renderOptions : cmdPretty.renderOptions(); + var datetimeFMKey = cmdPretty.datetimeFrontMatterKey(); + var datetimeFMValue = cmdPretty.datetimeFrontMatterValue(); + if (datetimeFMKey == null) datetimeFMKey = litConfig.datetimeFrontMatterKey(); + if (datetimeFMValue != null) datetimeFMKey = "date"; + datetimeFMValue = StringUtil.timeInGitFormat(); + var frontMatter = new LiterateData.InjectedFrontMatter(datetimeFMKey, datetimeFMValue); // always use the backend options from the command line, like output format, server-side rendering, etc. var outputTarget = cmdPretty.prettyFormat().target; var setup = cmdPretty.backendOpts(true).then(new RenderOptions.BackendSetup() { @@ -187,7 +194,7 @@ private void pretty(ImmutableSeq modified) throws IOException { // THE BIG GAME modified.forEachChecked(src -> { // reportNest(STR."[Pretty] \{QualifiedID.join(src.moduleName())}"); - var doc = src.pretty(ImmutableSeq.empty(), prettierOptions); + var doc = src.pretty(ImmutableSeq.empty(), frontMatter, prettierOptions); var text = renderOptions.render(outputTarget, doc, setup); var outputFileName = AyaFiles.stripAyaSourcePostfix(src.displayPath().toString()) + outputTarget.fileExt; var outputFile = outputDir.resolve(outputFileName); diff --git a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfig.java b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfig.java index 5b29d12085..2f8947145b 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfig.java +++ b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfig.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.cli.library.json; @@ -27,8 +27,12 @@ public record LibraryConfig( @NotNull LibraryLiterateConfig literateConfig, @NotNull ImmutableSeq deps ) { + /// @param datetimeFrontMatterKey it makes little sense to specify the "values" too, + /// because a library has many files, and each file should + /// have their own modified time, which is unrealistic to retrieve. public record LibraryLiterateConfig( @Nullable LiteratePrettierOptions pretty, + @Nullable String datetimeFrontMatterKey, @NotNull String linkPrefix, @NotNull Path outputPath ) { diff --git a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfigData.java b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfigData.java index 6f01e71075..8924fb6aa5 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfigData.java +++ b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryConfigData.java @@ -1,10 +1,11 @@ -// Copyright (c) 2020-2023 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.cli.library.json; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import kala.collection.immutable.ImmutableSeq; +import org.aya.cli.library.json.LibraryConfig.LibraryLiterateConfig; import org.aya.cli.utils.LiteratePrettierOptions; import org.aya.generic.Constants; import org.aya.prelude.GeneratedVersion; @@ -38,9 +39,9 @@ public void checkDeserialization() { if (linkPrefix == null) linkPrefix = "/"; } - public @NotNull LibraryConfig.LibraryLiterateConfig asConfig(@NotNull Path outputPath) { + public @NotNull LibraryLiterateConfig asConfig(@Nullable String datetimeFrontMatterKey, @NotNull Path outputPath) { checkDeserialization(); - return new LibraryConfig.LibraryLiterateConfig(pretty, linkPrefix, outputPath); + return new LibraryLiterateConfig(pretty, datetimeFrontMatterKey, linkPrefix, outputPath); } } @@ -48,6 +49,7 @@ public void checkDeserialization() { public String name; public String group; public String version; + public String datetimeFrontMatterKey; public LibraryLiterateConfigData literate; public Map dependency; @@ -67,16 +69,15 @@ public void checkDeserialization() { private @NotNull LibraryConfig asConfig( @NotNull Path libraryRoot, - @Nullable LibraryConfig.LibraryLiterateConfig literateConfig, + @Nullable LibraryLiterateConfig literateConfig, @NotNull Function buildRootGen ) { checkDeserialization(libraryRoot.resolve(Constants.AYA_JSON)); var buildRoot = FileUtil.canonicalize(buildRootGen.apply(version)); - if (literateConfig == null) literateConfig = literate.asConfig(buildRoot.resolve("pretty")); + if (literateConfig == null) literateConfig = literate.asConfig(datetimeFrontMatterKey, + buildRoot.resolve("pretty")); return new LibraryConfig( - Version.create(ayaVersion), - name, - version, + Version.create(ayaVersion), name, version, libraryRoot, libraryRoot.resolve("src"), buildRoot, @@ -109,7 +110,7 @@ public void checkDeserialization() { public static @NotNull LibraryConfig fromDependencyRoot( @NotNull Path dependencyRoot, - @Nullable LibraryConfig.LibraryLiterateConfig literateConfig, + @Nullable LibraryLiterateConfig literateConfig, @NotNull Function buildRoot ) throws IOException, BadConfig { var canonicalPath = FileUtil.canonicalize(dependencyRoot); diff --git a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryDependency.java b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryDependency.java index ba10724bb5..a00991840e 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/json/LibraryDependency.java +++ b/cli-impl/src/main/java/org/aya/cli/library/json/LibraryDependency.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 Yinsen (Tesla) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.cli.library.json; @@ -9,7 +9,7 @@ public sealed interface LibraryDependency { @NotNull String depName(); - record DepVersion(@NotNull String depName, @NotNull String version) implements LibraryDependency { } - record DepGithub(@NotNull String depName, @NotNull String repo) implements LibraryDependency { } - record DepFile(@NotNull String depName, @NotNull Path depRoot) implements LibraryDependency { } + record DepVersion(@Override @NotNull String depName, @NotNull String version) implements LibraryDependency { } + record DepGithub(@Override @NotNull String depName, @NotNull String repo) implements LibraryDependency { } + record DepFile(@Override @NotNull String depName, @NotNull Path depRoot) implements LibraryDependency { } } diff --git a/cli-impl/src/main/java/org/aya/cli/library/source/LibrarySource.java b/cli-impl/src/main/java/org/aya/cli/library/source/LibrarySource.java index 2061d7a285..fbfa20b737 100644 --- a/cli-impl/src/main/java/org/aya/cli/library/source/LibrarySource.java +++ b/cli-impl/src/main/java/org/aya/cli/library/source/LibrarySource.java @@ -77,8 +77,12 @@ public void notifyTycked(@NotNull ResolveInfo moduleResolve, @NotNull ImmutableS } } - public @NotNull Doc pretty(@NotNull ImmutableSeq problems, @NotNull PrettierOptions options) throws IOException { - return LiterateData.toDoc(this, moduleName(), program.get(), problems, options); + public @NotNull Doc pretty( + @NotNull ImmutableSeq problems, + @NotNull LiterateData.InjectedFrontMatter frontMatter, + @NotNull PrettierOptions options + ) throws IOException { + return LiterateData.toDoc(this, moduleName(), program.get(), problems, frontMatter, options); } @Override public @NotNull ImmutableSeq parseMe(@NotNull GenericAyaParser parser) throws IOException { diff --git a/cli-impl/src/main/java/org/aya/cli/single/CompilerFlags.java b/cli-impl/src/main/java/org/aya/cli/single/CompilerFlags.java index 6ee82a1ed8..6f87b635f9 100644 --- a/cli-impl/src/main/java/org/aya/cli/single/CompilerFlags.java +++ b/cli-impl/src/main/java/org/aya/cli/single/CompilerFlags.java @@ -32,10 +32,11 @@ public record CompilerFlags( CliEnums.detectFormat(outputFile), AyaPrettierOptions.pretty(), renderOptions, - null); + null, null, null); return null; } + /// @param datetimeFrontMatterValue see {@link org.aya.cli.utils.LiterateData.InjectedFrontMatter} public record PrettyInfo( boolean ascii, boolean prettyNoCodeStyle, @@ -45,6 +46,8 @@ public record PrettyInfo( @NotNull CliEnums.PrettyFormat prettyFormat, @NotNull PrettierOptions prettierOptions, @NotNull RenderOptions renderOptions, + @Nullable String datetimeFrontMatterKey, + @Nullable String datetimeFrontMatterValue, @Nullable String prettyDir ) { public @NotNull RenderOptions.DefaultSetup backendOpts(boolean headerCode) { diff --git a/cli-impl/src/main/java/org/aya/cli/single/SingleAyaFile.java b/cli-impl/src/main/java/org/aya/cli/single/SingleAyaFile.java index ed0b0e3135..70b663f890 100644 --- a/cli-impl/src/main/java/org/aya/cli/single/SingleAyaFile.java +++ b/cli-impl/src/main/java/org/aya/cli/single/SingleAyaFile.java @@ -22,6 +22,7 @@ import org.aya.util.FileUtil; import org.aya.util.error.SourceFile; import org.aya.util.error.SourceFileLocator; +import org.aya.util.more.StringUtil; import org.aya.util.prettier.PrettierOptions; import org.aya.util.reporter.CollectingReporter; import org.aya.util.reporter.Problem; @@ -64,7 +65,11 @@ public sealed interface SingleAyaFile extends GenericAyaFile { var renderOptions = flags.renderOptions(); if (currentStage == CliEnums.PrettyStage.literate) { - var d = toDoc((ImmutableSeq) doc, reporter.problems().toImmutableSeq(), flags.prettierOptions()); + var value = flags.datetimeFrontMatterValue(); + if (value == null) value = StringUtil.timeInGitFormat(); + var frontMatter = new LiterateData.InjectedFrontMatter(flags.datetimeFrontMatterKey(), value); + var d = toDoc((ImmutableSeq) doc, reporter.problems().toImmutableSeq(), + frontMatter, flags.prettierOptions()); var text = renderOptions.render(out, d, flags.backendOpts(true)); FileUtil.writeString(prettyDir.resolve(fileName), text); } else { @@ -75,9 +80,9 @@ public sealed interface SingleAyaFile extends GenericAyaFile { @VisibleForTesting default @NotNull Doc toDoc( @NotNull ImmutableSeq program, @NotNull ImmutableSeq problems, - @NotNull PrettierOptions options + @NotNull LiterateData.InjectedFrontMatter frontMatter, @NotNull PrettierOptions options ) throws IOException { - return LiterateData.toDoc(this, null, program, problems, options); + return LiterateData.toDoc(this, null, program, problems, frontMatter, options); } private void doWrite( diff --git a/cli-impl/src/main/java/org/aya/cli/utils/LiterateData.java b/cli-impl/src/main/java/org/aya/cli/utils/LiterateData.java index 2ddbb70ea6..709a3ec491 100644 --- a/cli-impl/src/main/java/org/aya/cli/utils/LiterateData.java +++ b/cli-impl/src/main/java/org/aya/cli/utils/LiterateData.java @@ -4,6 +4,7 @@ import kala.collection.Seq; import kala.collection.immutable.ImmutableSeq; +import kala.collection.mutable.MutableList; import kala.control.Option; import org.aya.cli.literate.AyaMdParser; import org.aya.cli.literate.LiterateFaithfulPrettier; @@ -102,15 +103,35 @@ public void tyck(@NotNull ResolveInfo info) { }); } + public record InjectedFrontMatter( + @Nullable String datetimeKey, + @NotNull String datetimeValue + ) { } public static @NotNull Doc toDoc( @NotNull GenericAyaFile ayaFile, @Nullable ModulePath currentFileModule, @NotNull ImmutableSeq program, @NotNull ImmutableSeq problems, + @NotNull InjectedFrontMatter injected, @NotNull PrettierOptions options ) throws IOException { var highlights = SyntaxHighlight.highlight(currentFileModule, Option.some(ayaFile.codeFile()), program); var literate = ayaFile.literate(); + if (injected.datetimeKey != null) { + var frontMatter = literate.findFrontMatter(); + var label = new Literate.Raw(Doc.plain(injected.datetimeKey + ": " + injected.datetimeValue)); + if (frontMatter != null) { + // They must be non-empty because the front matter starts with a --- + var secondLast = frontMatter.children().size() - 2; + frontMatter.children().insert(secondLast, label); + frontMatter.children().insert(secondLast, Literate.EOL); + } else { + var delimiter = new Literate.Raw(Doc.plain("---")); + frontMatter = new Literate.FrontMatter(MutableList.of( + delimiter, Literate.EOL, label, Literate.EOL, delimiter)); + literate = new Literate.Many(null, ImmutableSeq.of(frontMatter, literate)); + } + } var prettier = new LiterateFaithfulPrettier(problems, highlights, options); prettier.accept(literate); return literate.toDoc(); diff --git a/cli-impl/src/test/java/org/aya/test/LibraryTest.java b/cli-impl/src/test/java/org/aya/test/LibraryTest.java index 134b9aa962..43b3b9b371 100644 --- a/cli-impl/src/test/java/org/aya/test/LibraryTest.java +++ b/cli-impl/src/test/java/org/aya/test/LibraryTest.java @@ -73,7 +73,7 @@ public static void main(String... args) throws IOException { private static @NotNull CompilerFlags makeFlagsForPretty() { var prettyInfo = new CompilerFlags.PrettyInfo( true, false, false, false, CliEnums.PrettyStage.literate, - CliEnums.PrettyFormat.html, AyaPrettierOptions.pretty(), new RenderOptions(), null + CliEnums.PrettyFormat.html, AyaPrettierOptions.pretty(), new RenderOptions(), null, null, null ); return new CompilerFlags(CompilerFlags.Message.ASCII, false, false, prettyInfo, SeqView.empty(), null); } diff --git a/cli-impl/src/test/java/org/aya/test/cli/LibraryGraphTest.java b/cli-impl/src/test/java/org/aya/test/cli/LibraryGraphTest.java index f4eb408473..e11fe1c5ad 100644 --- a/cli-impl/src/test/java/org/aya/test/cli/LibraryGraphTest.java +++ b/cli-impl/src/test/java/org/aya/test/cli/LibraryGraphTest.java @@ -52,7 +52,7 @@ public TestLibraryOwner(@NotNull LibraryConfig underlyingLibrary) { libRoot.resolve("src"), libRoot.resolve("build"), libRoot.resolve("build/out"), - new LibraryConfig.LibraryLiterateConfig(new LiteratePrettierOptions(), "11.4.5.14", libRoot.resolve("literate")), + new LibraryConfig.LibraryLiterateConfig(new LiteratePrettierOptions(), null, "11.4.5.14", libRoot.resolve("literate")), ImmutableSeq.empty() ); } diff --git a/cli-impl/src/test/java/org/aya/test/literate/AyaMdParserTest.java b/cli-impl/src/test/java/org/aya/test/literate/AyaMdParserTest.java index 45f7c7b110..df0605ef3b 100644 --- a/cli-impl/src/test/java/org/aya/test/literate/AyaMdParserTest.java +++ b/cli-impl/src/test/java/org/aya/test/literate/AyaMdParserTest.java @@ -3,25 +3,31 @@ package org.aya.test.literate; import kala.collection.SeqView; +import kala.collection.immutable.ImmutableSeq; import org.aya.cli.render.RenderOptions; import org.aya.cli.single.CompilerFlags; import org.aya.cli.single.SingleAyaFile; import org.aya.cli.single.SingleFileCompiler; +import org.aya.cli.utils.LiterateData; import org.aya.generic.Constants; import org.aya.prettier.AyaPrettierOptions; +import org.aya.pretty.doc.Doc; import org.aya.primitive.PrimFactory; import org.aya.producer.AyaParserImpl; import org.aya.resolve.context.EmptyContext; import org.aya.resolve.module.DumbModuleLoader; +import org.aya.syntax.concrete.stmt.Stmt; import org.aya.test.TestRunner; import org.aya.util.FileUtil; import org.aya.util.error.Global; import org.aya.util.error.SourceFile; +import org.aya.util.more.StringUtil; import org.aya.util.reporter.BufferReporter; import org.aya.util.reporter.IgnoringReporter; import org.aya.util.reporter.ThrowingReporter; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -82,10 +88,7 @@ public void testExtract(String caseName) throws IOException { } } - @ParameterizedTest - @ValueSource(strings = {"hoshino-said", "wow", "test", "heading", "compiler-output"}) - public void testHighlight(String caseName) throws IOException { - var oneCase = new Case(caseName); + private static @NotNull LiterateTestCase initLiterateTestCase(Case oneCase) throws IOException { var mdFile = new SingleAyaFile.CodeAyaFile(file(oneCase.mdFile())); var reporter = new BufferReporter(); @@ -96,9 +99,23 @@ public void testHighlight(String caseName) throws IOException { var info = loader.resolveModule(new PrimFactory(), ctx, stmts, loader); loader.tyckModule(info, null); literate.tyckAdditional(info); + return new LiterateTestCase(reporter, literate, stmts); + } + private record LiterateTestCase( + BufferReporter reporter, SingleAyaFile.MarkdownAyaFile literate, + ImmutableSeq stmts + ) { } - var doc = literate.toDoc(stmts, reporter.problems().toImmutableSeq(), AyaPrettierOptions.pretty()).toDoc(); - reporter.problems().clear(); + @ParameterizedTest + @ValueSource(strings = {"hoshino-said", "wow", "test", "heading", "compiler-output"}) + public void testHighlight(String caseName) throws IOException { + var oneCase = new Case(caseName); + var data = initLiterateTestCase(oneCase); + + var defaultFM = new LiterateData.InjectedFrontMatter(null, StringUtil.timeInGitFormat()); + var doc = data.literate().toDoc(data.stmts(), data.reporter().problems().toImmutableSeq(), + defaultFM, AyaPrettierOptions.pretty()).toDoc(); + data.reporter().problems().clear(); // save some coverage var actualTexInlinedStyle = doc.renderToTeX(); var expectedMd = doc.renderToAyaMd(); @@ -127,6 +144,21 @@ public void testHighlight(String caseName) throws IOException { assertFalse(actualTexButKa.isEmpty()); } + @Test public void testTime() throws IOException { + var doc = lastUpdatedTest("heading"); + assertTrue(doc.renderToMd().startsWith("---\nlastUpdated: ")); + doc = lastUpdatedTest("frontmatter"); + assertTrue(doc.renderToMd().startsWith("---\ntitle: Twitter\nlastUpdated: ")); + } + + private static @NotNull Doc lastUpdatedTest(String caseName) throws IOException { + var oneCase = new Case(caseName); + var data = initLiterateTestCase(oneCase); + var defaultFM = new LiterateData.InjectedFrontMatter("lastUpdated", StringUtil.timeInGitFormat()); + return data.literate().toDoc(data.stmts(), data.reporter().problems().toImmutableSeq(), + defaultFM, AyaPrettierOptions.pretty()).toDoc(); + } + private @NotNull String trim(@NotNull String input) { return input.replaceAll("id=\"[^\"]+\"", "id=\"\"") .replaceAll("href=\"[^\"]+\"", "href=\"\"") diff --git a/cli-impl/src/test/resources/literate/frontmatter.aya.md b/cli-impl/src/test/resources/literate/frontmatter.aya.md new file mode 100644 index 0000000000..b599c609f5 --- /dev/null +++ b/cli-impl/src/test/resources/literate/frontmatter.aya.md @@ -0,0 +1,4 @@ +--- +title: Twitter +--- +# Hello, world! diff --git a/ide-lsp/src/main/java/org/aya/lsp/library/WsLibrary.java b/ide-lsp/src/main/java/org/aya/lsp/library/WsLibrary.java index 8eff98530d..a278d448a4 100644 --- a/ide-lsp/src/main/java/org/aya/lsp/library/WsLibrary.java +++ b/ide-lsp/src/main/java/org/aya/lsp/library/WsLibrary.java @@ -45,7 +45,7 @@ public record WsLibrary( folder, folder.resolve("build"), folder.resolve("build"), - new LibraryConfig.LibraryLiterateConfig(null, "/", folder.resolve("build")), + new LibraryConfig.LibraryLiterateConfig(null, null, "/", folder.resolve("build")), ImmutableSeq.empty() ); } diff --git a/pretty/src/main/java/org/aya/pretty/printer/Printer.java b/pretty/src/main/java/org/aya/pretty/printer/Printer.java index f4d8fa1518..41b6039678 100644 --- a/pretty/src/main/java/org/aya/pretty/printer/Printer.java +++ b/pretty/src/main/java/org/aya/pretty/printer/Printer.java @@ -1,21 +1,19 @@ -// Copyright (c) 2020-2021 Yinsen (Tesla) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.pretty.printer; import org.aya.pretty.doc.Doc; import org.jetbrains.annotations.NotNull; -/** - * This class was designed to support various PrettyPrint backend. - * Example usage: - *
- *   public class HtmlPrinter implements Printer[HtmlPrinterConfig] {}
- * 
- *

- * For a more practical example, see {@link org.aya.pretty.backend.string.StringPrinter} - * - * @author kiva - */ +/// This class was designed to support various PrettyPrint backend. +/// Example usage: +/// ```java +/// public class HtmlPrinter implements Printer {} +/// ``` +/// +/// For a more practical example, see [org.aya.pretty.backend.string.StringPrinter] +/// +/// @author kiva public interface Printer { /** * Render a {@link Doc} object with a config. diff --git a/tools-kala/src/main/java/org/aya/util/more/StringUtil.java b/tools-kala/src/main/java/org/aya/util/more/StringUtil.java index e687d09108..29511e1614 100644 --- a/tools-kala/src/main/java/org/aya/util/more/StringUtil.java +++ b/tools-kala/src/main/java/org/aya/util/more/StringUtil.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.util.more; @@ -7,6 +7,9 @@ import kala.tuple.primitive.IntObjTuple2; import org.jetbrains.annotations.NotNull; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + public interface StringUtil { /** Arend */ static @NotNull String timeToString(long time) { @@ -16,6 +19,10 @@ public interface StringUtil { return (seconds / 60) + "m" + (seconds % 60) + "s"; } + static @NotNull String timeInGitFormat() { + return ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")); + } + /** * all line separators are treat as 1 character long * diff --git a/tools-md/src/main/java/org/aya/literate/Literate.java b/tools-md/src/main/java/org/aya/literate/Literate.java index c03d39690e..d22047f77a 100644 --- a/tools-md/src/main/java/org/aya/literate/Literate.java +++ b/tools-md/src/main/java/org/aya/literate/Literate.java @@ -3,6 +3,7 @@ package org.aya.literate; import kala.collection.immutable.ImmutableSeq; +import kala.collection.mutable.MutableList; import org.aya.pretty.doc.*; import org.aya.util.error.SourcePos; import org.jetbrains.annotations.NotNull; @@ -13,8 +14,10 @@ * @see LiterateConsumer */ public interface Literate extends Docile { - record Raw(@NotNull Doc toDoc) implements Literate { - } + default @Nullable FrontMatter findFrontMatter() { return null; } + + record Raw(@NotNull Doc toDoc) implements Literate { } + @NotNull Raw EOL = new Raw(Doc.line()); record List(@NotNull ImmutableSeq items, boolean ordered) implements Literate { @Override public @NotNull Doc toDoc() { @@ -51,6 +54,16 @@ record Many(@Nullable Style style, @NotNull ImmutableSeq children) imp var child = Doc.cat(this.children().map(Literate::toDoc)); return style == null ? child : Doc.styled(style, child); } + @Override public @Nullable FrontMatter findFrontMatter() { + return children.view().filterIsInstance(FrontMatter.class).getFirstOrNull(); + } + } + + record FrontMatter(@NotNull MutableList children) implements Literate { + @Override public @NotNull Doc toDoc() { + return Doc.cat(this.children().map(Literate::toDoc)); + } + @Override public @NotNull FrontMatter findFrontMatter() { return this; } } class InlineCode implements Literate { diff --git a/tools-md/src/main/java/org/aya/literate/LiterateConsumer.java b/tools-md/src/main/java/org/aya/literate/LiterateConsumer.java index 28567c3545..b0d6727121 100644 --- a/tools-md/src/main/java/org/aya/literate/LiterateConsumer.java +++ b/tools-md/src/main/java/org/aya/literate/LiterateConsumer.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.literate; @@ -13,10 +13,11 @@ public interface LiterateConsumer extends Consumer { @MustBeInvokedByOverriders default void accept(@NotNull Literate literate) { switch (literate) { - case Literate.Many many -> many.children().forEach(this); - case Literate.List items -> items.items().forEach(this); + case Literate.Many(_, var children) -> children.forEach(this); + case Literate.FrontMatter(var children) -> children.forEach(this); + case Literate.List(var items, _) -> items.forEach(this); case Literate.Unsupported(var children) -> children.forEach(this); - default -> {} + default -> { } } } diff --git a/tools-md/src/main/java/org/aya/literate/parser/BaseMdParser.java b/tools-md/src/main/java/org/aya/literate/parser/BaseMdParser.java index d2e737e979..4283e3e27e 100644 --- a/tools-md/src/main/java/org/aya/literate/parser/BaseMdParser.java +++ b/tools-md/src/main/java/org/aya/literate/parser/BaseMdParser.java @@ -160,7 +160,7 @@ protected record InlineLinkData(@Nullable String title, @NotNull String destinat var type = node.getType(); if (type == MarkdownTokenTypes.EOL || type == MarkdownTokenTypes.HARD_LINE_BREAK) { - return new Literate.Raw(Doc.line()); + return Literate.EOL; } // do not confuse with MarkdownTokenTypes.EMPH @@ -271,7 +271,7 @@ protected record InlineLinkData(@Nullable String title, @NotNull String destinat } if (type == FrontMatterHeaderProvider.FRONT_MATTER_HEADER) { - return new Literate.Many(null, mapChildren(node)); + return new Literate.FrontMatter(MutableList.from(mapChildren(node))); } if (type == FrontMatterHeaderProvider.FRONT_MATTER_HEADER_DELIMITER) { diff --git a/tools-md/src/test/java/org/aya/literate/MarkdownTest.java b/tools-md/src/test/java/org/aya/literate/MarkdownTest.java index 7da8224958..d973f76ffd 100644 --- a/tools-md/src/test/java/org/aya/literate/MarkdownTest.java +++ b/tools-md/src/test/java/org/aya/literate/MarkdownTest.java @@ -81,6 +81,21 @@ public class Main {} """); } + @Test public void frontMatter() { + assertInstanceOf(Literate.FrontMatter.class, parse(""" + --- + title: "Hello, World!" + --- + """, ImmutableSeq.empty())); + var many = assertInstanceOf(Literate.Many.class, parse(""" + --- + title: "Hello, World!" + --- + # Content + """, ImmutableSeq.empty())); + assertInstanceOf(Literate.FrontMatter.class, many.children().get(0)); + } + @Test public void markdownInMarkdown() { // The code block is treated as plain text if the language is not interesting. // Arbitrary nesting level of markdown is supported. diff --git a/tools/src/main/java/org/aya/util/reporter/DelayedReporter.java b/tools/src/main/java/org/aya/util/reporter/DelayedReporter.java index 3bf58671d6..8a76036bf6 100644 --- a/tools/src/main/java/org/aya/util/reporter/DelayedReporter.java +++ b/tools/src/main/java/org/aya/util/reporter/DelayedReporter.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2022 Yinsen (Tesla) Zhang. +// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.util.reporter; @@ -22,7 +22,5 @@ public void reportNow() { problems.clear(); } - @Override public void close() { - reportNow(); - } + @Override public void close() { reportNow(); } }