Skip to content

Commit

Permalink
File generation docs (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
mfwgenerics authored Jan 14, 2023
1 parent ba89e88 commit 444f282
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 54 deletions.
75 changes: 53 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,73 @@
# Markout

Markout is a library for generating markdown files and directories from Kotlin
Markout is a library for generating files, directories and Markdown documentation from Kotlin.
It is designed for generating GitHub Flavored Markdown docs that live alongside code.
Using [Kapshot](https://github.com/mfwgenerics/kapshot) with this project
allows literate programming and "executable documentation", enabling developers
to ensure that documentation remains correct and up to date.

## Use
1. [Getting Started](#getting-started)
2. [Usage](#usage)

## Getting Started

Add the markout dependency

```kotlin
/* build.gradle.kts */
dependencies {
implementation("io.koalaql:markout:0.0.2")
implementation("io.koalaql:markout:0.0.3")
}
```

## Syntax
#### File Generation

1. [Basic Syntax](docs/BASIC.md)
2. [Extended Syntax](docs/EXTENDED.md)

## Example
If you want to use markout as a documentation generator, call
the `markout` function directly from your main method. Pass a path
to the directory where you want markout to generate files.
The path can be relative or absolute.

```kotlin
h1 { t("Hello "); b("Markout!") }
fun main() = markout(Path(".")) {
markdown("hello") {
p("This file was generated using markout")

p {
i("Hello ") + "World!"
}
}
}
```

Currently the Gradle application plugin is the best way to run a standalone markout project

p("Example paragraph")
```shell
./gradlew :my-project:run
```

#### Markdown Strings

ol {
li("List")
li("Of")
li("Items")
If you only want to use markout to generate Markdown strings then you can use
`markdownString`

```kotlin
markdownString {
h1("My Markdown")

-"Text with some "+i("italics")+"."
}
```

Will produce the following markdown
The above will produce the String

```markdown
# My Markdown

```md
# Hello **Markout!**
Text with some *italics*.
```

Example paragraph
## Usage

1. List
2. Of
3. Items
```
1. [File Generation](docs/FILES.md)
2. [Basic Markdown](docs/BASIC.md)
3. [Extended Markdown](docs/EXTENDED.md)
1 change: 1 addition & 0 deletions docs/.markout
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
BASIC.md
EXTENDED.md
FILES.md
75 changes: 75 additions & 0 deletions docs/FILES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# File Generation

Markout can run in one of two modes

1. [Apply Mode](#apply-mode)
2. [Expect Mode](#expect-mode)

## Apply Mode

Apply is the default mode. It generates files and directories and deletes
previously generated files and directories that were not regenerated.

### Files and Directories

Markout provides a straightforward DSL for generating files and directories

```kotlin
markout(Path("..")) {
directory("my-directory") {
directory("inner") {
file("inner.txt", "another plain text file")
}

file("plain.txt", "the contents of a plain text file")

file("circle.svg", """
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" fill="black" />
</svg>
""".trimIndent())
}

markdown("readme") {
-"A markdown file"
-"The .md prefix is automatically added to the filename"
}
}
```

When this code is run it generates the following file tree

```
.markout
my-directory
├─ .markout
├─ inner
│ ├─ .markout
│ └─ inner.txt
├─ plain.txt
└─ circle.svg
readme.md
```

### File Tracking

When Markout generates directories it includes a `.markout` file. This is
how Markout keeps track of generated files. It should always be checked
into git. Markout will never change or delete an existing file or directory
unless it is tracked in `.markout`

File tracking allows regular files to be mixed in with generated ones.
For example, you might mix handwritten markdown into your docs directory.

## Expect Mode

Running Markout in expect mode will cause it to fail when it encounters changes.
This allows you to check that files have been generated and are consistent
with the code. It is intended for use in CI workflows.

To use Expect mode, run markout with the `MARKOUT_MODE` environment variable set to `expect`.

```shell
MARKOUT_MODE=expect ./gradlew :readme:run
```
10 changes: 5 additions & 5 deletions markout/src/main/kotlin/io/koalaql/markout/Markout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface Markout {
fun file(name: String, contents: String)
}

private fun buildOutput(builder: Markout.() -> Unit): OutputDirectory = OutputDirectory {
fun buildOutput(builder: Markout.() -> Unit): OutputDirectory = OutputDirectory {
val entries = linkedMapOf<String, Output>()

object : Markout {
Expand Down Expand Up @@ -194,13 +194,13 @@ private fun Output.expect(
}
}

private fun executionModeProperty(): ExecutionMode {
val key = "MARKOUT_MODE"
val MODE_ENV_VAR = "MARKOUT_MODE"

return when (val value = System.getenv(key)) {
private fun executionModeProperty(): ExecutionMode {
return when (val value = System.getenv(MODE_ENV_VAR)) {
null, "", "apply" -> ExecutionMode.APPLY
"expect" -> ExecutionMode.EXPECT
else -> error("unexpected value `$value` for property $key")
else -> error("unexpected value `$value` for property $MODE_ENV_VAR")
}
}

Expand Down
2 changes: 2 additions & 0 deletions markout/src/main/kotlin/io/koalaql/markout/md/Markdown.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ interface Markdown: MarkdownBlock {
fun p(block: MarkdownBlock.() -> Unit)
@MarkoutDsl
fun p(text: String) = p { t(text) }
@MarkoutDsl
fun p() = p { }

@MarkoutDsl
fun hr()
Expand Down
81 changes: 81 additions & 0 deletions readme/src/main/kotlin/FileGen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import io.koalaql.markout.MODE_ENV_VAR
import io.koalaql.markout.Markout
import io.koalaql.markout.buildOutput
import io.koalaql.markout.md.markdown
import java.nio.file.Path
import kotlin.io.path.Path

fun Markout.fileGen() = markdown("FILES") {
h1("File Generation")

-"Markout can run in one of two modes"

sectioned {
section("Apply Mode") {
-"Apply is the default mode. It generates files and directories and deletes"
-"previously generated files and directories that were not regenerated."

h3("Files and Directories")

-"Markout provides a straightforward DSL for generating files and directories"

fun markout(ignored: Path, builder: Markout.() -> Unit) =
buildOutput(builder)

val output = code {
markout(Path("..")) {
directory("my-directory") {
directory("inner") {
file("inner.txt", "another plain text file")
}

file("plain.txt", "the contents of a plain text file")

file("circle.svg", """
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" fill="black" />
</svg>
""".trimIndent())
}

markdown("readme") {
-"A markdown file"
-"The .md prefix is automatically added to the filename"
}
}
}

-"When this code is run it generates the following file tree"

code(drawFileTree(output))

h3("File Tracking")

p {
-"When Markout generates directories it includes a `.markout` file. This is"
-"how Markout keeps track of generated files. It should always be checked"
-"into git. Markout will never change or delete an existing file or directory"
-"unless it is tracked in `.markout`"
}

p {
-"File tracking allows regular files to be mixed in with generated ones."
-"For example, you might mix handwritten markdown into your docs directory."
}
}

section("Expect Mode") {
p {
-"Running Markout in expect mode will cause it to fail when it encounters changes."
-"This allows you to check that files have been generated and are consistent"
-"with the code. It is intended for use in CI workflows."
}

p {
-"To use Expect mode, run markout with the `$MODE_ENV_VAR` environment variable set to `expect`."
}

code("shell", "$MODE_ENV_VAR=expect ./gradlew :readme:run")
}
}
}
Loading

0 comments on commit 444f282

Please sign in to comment.