I am writing a technical book for Java programming in AsciiDoc format, using the AsciiDoctor plugin for the IntelliJ IDE.
I have seen how to include snippets of code from a Java source code file.
I have heard of people also executing Java source code, then capturing output from that code for inclusion within the AsciiDoc content. But I have no idea how to do this.
Is running Java code and capturing output into AsciiDoc really possible? If so:
- Should the Java code be included in a single project also containing the AsciiDoc content files? Or should the Java code and the AsciiDoc-formatted book content be contained in two separate distinct projects?
- If the Java project is driven by Maven or Gradle, how do we hook AsciiDoc into the build process? When changing the Java source code, can we update the code snippet and freshen the output over in the AsciiDoc-formatted content files?
Given how many points you have, I'm guessing you probably have some idea how to do this, and are hoping for something clever. But something like writing compiling books might be niche enough that you have to DIY something yourself. I'm kinda spitballing here about what I would personally do, so apologies in advance if the answer seems meh quality.
This problem can be broken into a few parts:
Representing the code
For each code snippet, I'd write a little class with a main method, and then demarcate the part you want to extract using comments, similar to how many static analysis tools use comments to toggle off/on.
This would be enough to handle snippets that directly stuff out. If you want your snippets to be expressions (instead of statements/methods)...that might be trickier. But even in the above case, you have options; you could return something and print it from the main method (so you don't have printlns in every snippet) or you could move the snippet comments inside the method.
Running the code
If you have a file
src/main/org/example/MySnippet.java, you really just want to runorg.example.MySnippet's main method. If you're using Gradle, the most galaxy-brain way to do this would probably be writing a small Gradle plugin that uses annotation processing, but there are scrappier ways to do this. I'd see about writing a Gradle task to scan for files in some particular directory (e.g./src/main/org/example/*.java), convert those file paths into class names, and then dynamically generating JavaExec tasks during Gradle's configuration phase that will run every main method and write their output to different files. JavaExec has astandardOutputfield that should prove useful. Fiddling with the whitespace indents might be a pain, in which case I suppose you could simply not indent those lines of code.For each file found this way, you'd want to slice out those comments mentioned in the previous step, and perhaps write this out to the same stdout file before the JavaExec has a chance to run...depending on how exactly you want your snippets to look. In this case, it could be "Here's the code, then a horizontal rule, then the output". Emitting the output inline or near the line that executed it would be trickier.
Converting to AsciiDoc
Theoretically, at this point you have
build/generated/snippets*.txtfiles or something with just the raw output. You could have another task that converts these txt files to adoc snippets, or...the previous step could just emit valid adoc snippets to begin with, which would save some trouble.Wiring into AsciiDoc
You could, and long term maybe should, combine these into a single Gradle project, but it'd also be easy to make them separate projects that must be checked out next to each other --
book/andbook_samples/as sibling directories. In your book, you'd just need to import ../../../ enough times to get into the build/generated/* of the other project.I wasn't able to be too prescriptive here, but I hope this is still a good lead. You'll definitely have to learn some build tooling (e.g. custom Gradle/Maven tasks) if you haven't already, unless you do end up finding a tool that already does this.