Skip to content

Commit

Permalink
Merge pull request #336 from jp7677/asciidoc-support
Browse files Browse the repository at this point in the history
Asciidoc support
  • Loading branch information
jp7677 authored Oct 23, 2023
2 parents e9e3ac7 + 45efb9b commit 01332d0
Show file tree
Hide file tree
Showing 31 changed files with 811 additions and 211 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ is generated from the example workspace in this repository.
- Easy browsing through the site by clicking on software system and container elements in the diagrams.
- Start a development server which generates a site, serves it and updates the site automatically whenever a file that's
part of the Structurizr workspace changes.
- Include documentation (in Markdown format) in the generated site. Both workspace level documentation and software
- Include documentation (in Markdown or AsciiDoc format) in the generated site. Both workspace level documentation and software
system level documentation are included in the site.
- Include ADR's in the generated site. Again, both workspace level ADR's and software system level ADR's are included in
the site.
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies {
implementation("net.sourceforge.plantuml:plantuml:1.2023.12")

implementation("com.vladsch.flexmark:flexmark-all:0.64.8")
implementation("org.asciidoctor:asciidoctorj:2.5.10")
implementation("org.jsoup:jsoup:1.16.2")

implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.9.1")
Expand Down
6 changes: 3 additions & 3 deletions docs/example/workspace-docs/02-markdown-features.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Extended markdown features
## Extended Markdown features

This page showcases the ability to use extended markdown formating features in workspace documentation files. The full list of available extensions to standard commonmark markdown features is documented in the flexmark wiki [Extensions page](https://github.com/vsch/flexmark-java/wiki/Extensions).
This page showcases the ability to use extended Markdown formating features in workspace documentation files. The full list of available extensions to standard commonmark markdown features is documented in the flexmark wiki [Extensions page](https://github.com/vsch/flexmark-java/wiki/Extensions).

Most of these extended features have to be activated in your architecture model as a property in workspace views.

Expand All @@ -27,7 +27,7 @@ workspace {
}
```

### TableOfContents
### Table of Contents

`[TOC]` element which renders a table of contents

Expand Down
170 changes: 170 additions & 0 deletions docs/example/workspace-docs/03-asciidoc-features.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
= AsciiDoc features
:toc: macro
:imagesdir: ../assets
:tip-caption: 💡Tip

== AsciiDoc features 📌

This page showcases the ability to use AsciiDoc formating features in workspace documentation files. The full list of AsciiDoc features is documented in the https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/[Asciidoctor Syntax Reerence].

toc::[]

=== Embedding diagrams

Diagrams can be embedded using the `embed:` syntax:

[source, asciidoc]
----
image::embed:SystemLandscape[System Landscape Diagram]
----

See also: https://www.structurizr.com/help/documentation/diagrams

==== Example: Embedded diagram

image::embed:SystemLandscape[System Landscape Diagram]

=== Embedding static images

==== Example Embedded picture

When, for example, you would like to embed a nice picture which is located in the `pictures` directory under the assets directory, you can do that as follows:

[source, asciidoc]
----
image::/pictures/nice-picture.png[A nice picture]
----

https://www.flickr.com/photos/schmollmolch/4937297813/[Sun], by Christian Scheja

image::/pictures/nice-picture.png[A nice picture]

=== Embedding mermaid diagrams

==== Flowchart Diagram Example

[source, asciidoc]
-----
[source, mermaid]
----
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
----
-----

[source, mermaid]
----
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
----

==== Sequence Diagram Example

[source, asciidoc]
-----
[source, mermaid]
----
sequenceDiagram
participant Alice
participant Bob
Alice->>John: Hello John, how are you?
loop Healthcheck
John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts <br/>prevail!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
----
-----

[source, mermaid]
----
sequenceDiagram
participant Alice
participant Bob
Alice->>John: Hello John, how are you?
loop Healthcheck
John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts <br/>prevail!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
----

=== Tables

[source, asciidoc]
----
|===
|Column 1, Header Row |Column 2, Header Row
|Cell in column 1, row 1
|Cell in column 2, row 1
|Cell in column 1, row 2
|Cell in column 2, row 2
|===
----

This will be rendered as

|===
|Column 1, Header Row |Column 2, Header Row

|Cell in column 1, row 1
|Cell in column 2, row 1

|Cell in column 1, row 2
|Cell in column 2, row 2
|===

=== Admonition Blocks

Admonitions create block-styled side content.

NOTE: This is a note.

[TIP]
.Info
=====
Go to this URL to learn more about it:
* https://docs.asciidoctor.org/asciidoc/latest/blocks/admonitions/
CAUTION: This is Caution message!
WARNING: This is a Warning message!
=====

[IMPORTANT]
One more thing. Happy documenting!

=== Block quotes

[quote,attribution,citation title and information]
Quote or excerpt text

=== Checklist

[source, asciidoc]
----
* [*] checked
* [x] also checked
* [ ] not checked
* normal list item
----

will be rendered as:

* [*] checked
* [x] also checked
* [ ] not checked
* normal list item
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package nl.avisi.structurizr.site.generatr.site.model

import org.asciidoctor.Asciidoctor
import org.asciidoctor.ast.ContentNode
import org.asciidoctor.ast.Document
import org.asciidoctor.ast.StructuralNode
import org.asciidoctor.converter.ConverterFor
import org.asciidoctor.converter.StringConverter

val asciidoctor: Asciidoctor = Asciidoctor.Factory.create().apply {
javaConverterRegistry().register(AsciiDocTextConverter::class.java)
}

@ConverterFor("text")
class AsciiDocTextConverter(
backend: String?,
opts: Map<String?, Any?>?
) : StringConverter(backend, opts) {
// based on https://docs.asciidoctor.org/asciidoctorj/latest/write-converter/

override fun convert(node: ContentNode, transform: String?, o: Map<Any?, Any?>?): String? {
val transform1 = transform ?: node.nodeName
return if (node is Document)
node.content.toString()
else if (node is org.asciidoctor.ast.Section)
"${node.title}\n${node.content}"
else if (transform1 == "preamble" || transform1 == "paragraph")
(node as StructuralNode).content as String
else
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ import com.structurizr.documentation.Section
import com.vladsch.flexmark.ast.Heading
import com.vladsch.flexmark.ast.Paragraph
import com.vladsch.flexmark.parser.Parser
import org.asciidoctor.Options
import org.asciidoctor.SafeMode

private val parser = Parser.builder().build()

fun Decision.contentText(): String {
if (format != Format.Markdown)
return ""

return extractText(content)
fun Decision.contentText(): String = when (format) {
Format.Markdown -> markdownText(content)
Format.AsciiDoc -> asciidocText(content)
else -> ""
}

fun Section.contentText(): String {
if (format != Format.Markdown)
return ""

return extractText(content)
fun Section.contentText() = when (format) {
Format.Markdown -> markdownText(content)
Format.AsciiDoc -> asciidocText(content)
else -> ""
}

private fun extractText(content: String): String {
private fun markdownText(content: String): String {
val document = parser.parse(content)
if (!document.hasChildren())
return ""
Expand All @@ -42,3 +42,10 @@ private fun extractText(content: String): String {
)
.trim()
}

private fun asciidocText(content: String): String {
val options = Options.builder().safe(SafeMode.SERVER).backend("text").build()
val text = asciidoctor.convert(content, options)

return text.lines().joinToString(" ")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,35 @@ package nl.avisi.structurizr.site.generatr.site.model
import com.structurizr.documentation.Format
import com.structurizr.documentation.Section
import com.vladsch.flexmark.ast.Heading
import com.vladsch.flexmark.ast.Paragraph
import com.vladsch.flexmark.parser.Parser
import org.asciidoctor.Asciidoctor
import org.asciidoctor.Options
import org.asciidoctor.SafeMode

private val parser = Parser.builder().build()
private const val MAX_TITLE_LENGTH = 50

fun Section.contentTitle(): String {
if (format != Format.Markdown)
return "unsupported document"
fun Section.contentTitle(): String = when (format) {
Format.Markdown -> markdownTitle()
Format.AsciiDoc -> asciidocTitle()
else -> "unsupported document"
}

private fun Section.markdownTitle(): String {
val document = parser.parse(content)

if (!document.hasChildren())
return "untitled document"

val header = document.children.firstOrNull { it is Heading }?.let { it as Heading }
if (header != null)
return header.text.toString()

val paragraph = document.children.firstOrNull { it is Paragraph }?.let { it as Paragraph }?.chars?.toString()
if (paragraph != null)
return if (paragraph.length > MAX_TITLE_LENGTH) {
val whitespacePosition = paragraph.withIndex()
.filter { it.value.isWhitespace() }
.lastOrNull { it.index < MAX_TITLE_LENGTH }
?.index
paragraph.take(whitespacePosition ?: MAX_TITLE_LENGTH)
} else paragraph

return "unknown document"
return "untitled document"
}

private fun Section.asciidocTitle(): String {
val options = Options.builder().safe(SafeMode.SERVER).build()
val document = asciidoctor.load(content, options)

if (document.title != null && document.title.isNotEmpty())
return document.title

return "untitled document"
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package nl.avisi.structurizr.site.generatr.site.model

import com.structurizr.documentation.Format
import nl.avisi.structurizr.site.generatr.site.GeneratorContext
import org.intellij.lang.annotations.Language

class HomePageViewModel(generatorContext: GeneratorContext) : PageViewModel(generatorContext) {
override val pageSubTitle = if (generatorContext.workspace.name.isNotBlank()) "" else "Home"
override val url = url()

val content = markdownToHtml(
val content = toHtml(
this,
markdown = generatorContext.workspace.documentation.sections
content = generatorContext.workspace.documentation.sections
.firstOrNull { it.order == 1 }?.content ?: DEFAULT_HOMEPAGE_CONTENT,
format = generatorContext.workspace.documentation.sections
.firstOrNull { it.order == 1 }?.format ?: Format.Markdown,
svgFactory = generatorContext.svgFactory
)

Expand Down
Loading

0 comments on commit 01332d0

Please sign in to comment.