Document your go code using asciidoc. It allows you to have asciidoc markup in all code documentation. Asciidoc do support many plugins to e.g. render sequence diagrams, svg images, ERD, BPMN, RackDiag and many more.
One such component is kroki that renders the ascii into fine-art :).
🐬 Quick Install
go install github.com/mariotoffia/goasciidoc@latest
💡 See the plugins section below for examples on kroki rendered images
To generate documentation for this project as mydoc.adoc, do the following:
goasciidoc -o mydoc.adoc
The above will generate standard code documentation, internal and test is excluded. By default it renders a index with some defaults including a table of contents. Is is possible to override the contents by supplying a JSON string with overrides.
You may have more properties in the -c
(configuration) parameter, for example:
{
"author": "Mario Toffia",
"email": "[email protected]",
"web": "https://github.com/mariotoffia/goasciidoc",
"images": "../meta/assets",
"title": "Go Asciidoc Document Generator",
"toc": "Table of Contents",
"toclevel": 2
}
Everything is rendered using go templates and it is possible to override each of them using the -t
switch (or if in same folder using --templatedir
switch). Take a look at defaults/*.gtpl
to view how such may look like. It is standard go templates.
All code is parsed thus you may annotate with asciidoc wherever you want, e.g.
// HealthChecker is responsible for doing various health checks on patients.
//
// Its main flow is conceptualized on following sequence diagram
//
// [mermaid,config-override,svg]
// ....
// 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 prevail...
// John-->Alice: Great!
// John->Bob: How about you?
// Bob-->John: Jolly good!
// ....
type HealthChecker struct {
}
This installs the latest version. Use the repository tags to determine the version you want to install (if not latest).
go install github.com/mariotoffia/goasciidoc@latest
You may now use the goasciidoc
e.g. in the goasciidoc
repo by goasciidoc --stdout
. This will emit this project documentation onto the stdout. If you need help on flags and parameters jus do a goasciidoc --h
.
goasciidoc v0.4.1
Usage: goasciidoc [--out PATH] [--stdout] [--module PATH] [--internal] [--private] [--nonexported] [--test] [--noindex] [--notoc] [--indexconfig JSON] [--overrides OVERRIDES] [--list-template] [--out-template OUT-TEMPLATE] [--packagedoc FILEPATH] [--templatedir TEMPLATEDIR] [PATH [PATH ...]]
Positional arguments:
PATH Directory or files to be included in scan (if none, current path is used)
Options:
--out PATH, -o PATH The out filepath to write the generated document, default module path, file docs.adoc
--stdout If output the generated asciidoc to stdout instead of file
--module PATH, -m PATH
an optional folder or file path to module, otherwise current directory
--internal, -i If internal go code shall be rendered as well
--private, -p If files beneath directories starting with an underscore shall be included
--nonexported Renders Non exported as well as the exported. Default only Exported is rendered.
--test, -t If test code should be included
--noindex, -n If no index header shall be generated
--notoc Removes the table of contents if index document
--indexconfig JSON, -c JSON
JSON document to override the IndexConfig
--overrides OVERRIDES, -r OVERRIDES
name=template filepath to override default templates
--list-template Lists all default templates in the binary
--out-template OUT-TEMPLATE
outputs a template to stdout
--packagedoc FILEPATH, -d FILEPATH
set relative package search filepaths for package documentation
--templatedir TEMPLATEDIR
Loads template files *.gtpl from a directory, use --list to get valid names of templates
--help, -h display this help and exit
--version display version and exit
By default goasciidoc
will use overview.adoc or _design/overview.adoc to generate the package overview. If those are not found, it will default back to the golang package documentation (if any).
It is possible to set other search paths for those document. The search-path is relative the package path.
NOTE: That the path is a relative filepath i.e both directory and file. Directory may be omitted.
For example, look for package-overview.adoc in package folder instead of the default overview.adoc, _design/overview.adoc:
goasciidoc --stdout -d package-overview.adoc
There are a initial support for macros in goasciidoc, for example ${gad:current:fq} is supported and will substitute the macro to the current fully qualified path to the source file. This can be e.g. used for inclusions of source code.
Example Documentation
// ParseConfig to use when invoking ParseAny, ParseSingleFileWalker, and
// ParseSinglePackageWalker.
//
// .ParserConfig
// [source,go]
// ----
// include::${gad:current:fq}[tag=parse-config,indent=0]
// ----
// <1> These are usually excluded since many testcases is not documented anyhow
// <2> As of _go 1.16_ it is recommended to *only* use module based parsing
// tag::parse-config[]
type ParseConfig struct {
// Test denotes if test files (ending with _test.go) should be included or not
// (default not included)
Test bool // <1>
// Internal determines if internal folders are included or not (default not)
Internal bool
// UnderScore, when set to true it will include directories beginning with _
UnderScore bool
// Optional module to resolve fully qualified package paths
Module *GoModule // <2>
}
// end::parse-config[]
It will then get rendered as follows:
Macro | Description |
---|---|
${gad:current:fq} | The fully qualified path to file being processed. |
${gad:current:fqdir} | The fully qualified path to folder being processed. |
${gad:current:dir} | The directory name where being processed. |
${gad:current:file} | The file name where being processed. |
This project consists of a parser to parse go-code and a producer to produce asciidoc files from the code & code documentation. It bases its rendering system heavily on templates (asciidoc/template.go
) with some "sane" default so it may be rather easily overridden. The default templates is embedded in the binary from the defaults/*.gtpl
files.
To list the default templates just do goasciidoc --list-template
. Version 0.4.0 will list the following template names:
- interfaces
- interface
- consts
- typedeffunc
- package
- import
- typedefvars
- vars
- index
- function
- typedeffuncs
- functions
- structs
- struct
- typedefvars
- var
- const
- receivers
It is possible to retrieve the default templates (use list to get the template names) using a command switch --out-template NAME
, for example:
goasciidoc --out-template struct
The above outputs (for v0.0.6):
"=== {{.Struct.Name}}
[source, go]
----
{{.Struct.Decl}} {
{{- range .Struct.Fields}}
{{if .Nested}}{{.Nested.Name}}{{"\t"}}struct{{else}}{{tabify .Decl}}{{end}}
{{- end}}
}
----
{{.Struct.Doc}}
{{range .Struct.Fields}}{{if not .Nested}}
==== {{.Decl}}
{{.Doc}}
{{- end}}
{{end}}
{{range .Struct.Fields}}{{if .Nested}}{{render $ .Nested}}{{end}}{{end}}
"
If you're unhappy with one of the default templates, you may override it (one or more) using the -t FILEPATH
switch. It may be several -t
on same command if multiple overrides. The filepath is either relative or fully qualified filepath to a template file.
For example, overriding the package template can be done like this:
echo "== Override Package {{.File.FqPackage}}" > t.txt; goasciidoc -r package=t.txt --stdout; rm t.txt
In the stdout
you may observe, now, it has Override Package instead of Package as heading
== Override Package github.com/mariotoffia/goasciidoc/goparser
=== Imports
...
It is possible to set a template directory where goasciidoc
will search for files named (see list templates) and file extension .gtpl e.g. import.gtpl.
Example usage: goasciidoc --templatedir defaults
It reads all files and overrides those found, the rest is using the default. You can checkout the defaults folder (or copy as starting point) when you make your own layout. You can remove those not needed, and the defaults will kick in.
ls -l defaults
total 72
-rw-r--r-- 1 martoffi martoffi 104 Mar 19 21:33 const.gtpl
-rw-r--r-- 1 martoffi martoffi 256 Mar 19 21:33 consts.gtpl
-rw-r--r-- 1 martoffi martoffi 208 Mar 19 21:25 function.gtpl
-rw-r--r-- 1 martoffi martoffi 142 Mar 19 21:25 functions.gtpl
-rw-r--r-- 1 martoffi martoffi 159 Mar 19 21:33 import.gtpl
-rw-r--r-- 1 martoffi martoffi 623 Mar 19 21:24 index.gtpl
-rw-r--r-- 1 martoffi martoffi 307 Mar 19 21:26 interface.gtpl
-rw-r--r-- 1 martoffi martoffi 111 Mar 19 21:26 interfaces.gtpl
-rw-r--r-- 1 martoffi martoffi 220 Mar 19 21:24 package.gtpl
-rw-r--r-- 1 martoffi martoffi 148 Mar 19 21:27 receivers.gtpl
-rw-r--r-- 1 martoffi martoffi 562 Mar 19 21:26 struct.gtpl
-rw-r--r-- 1 martoffi martoffi 105 Mar 19 21:27 structs.gtpl
-rw-r--r-- 1 martoffi martoffi 92 Mar 19 21:32 typedeffunc.gtpl
-rw-r--r-- 1 martoffi martoffi 120 Mar 19 21:32 typedeffuncs.gtpl
-rw-r--r-- 1 martoffi martoffi 175 Mar 19 21:34 typedefvar.gtpl
-rw-r--r-- 1 martoffi martoffi 126 Mar 19 21:34 typedefvars.gtpl
-rw-r--r-- 1 martoffi martoffi 102 Mar 19 21:34 var.gtpl
-rw-r--r-- 1 martoffi martoffi 111 Mar 19 21:34 vars.gtpl
The package goparser
was taken from an open source project by zpatrick. It seemed abandoned so I've integrated it into this project (and extended it) and now it deviates rather much from it's earlier pure form ;). Many thanks @zpatrick!! That part has a MIT License.
copy.go
is created by Roland Singer [[email protected]] and is used for unit test. Many thanks @r0l1. You may find the original here.
Since asciidoc supports plugins, thus is very versatile, myself is using kroki that may render many types of diagrams (can be done online or offline using docker-compose). Below there are just a few of many, many diagrams that may be outputted just using kroki.
For example a sequence diagram based on the following text in your documentation
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 prevail...
John-->Alice: Great!
John->Bob: How about you?
Bob-->John: Jolly good!
will render the following sequence diagram
or are you into packet diagrams will the following text
packetdiag {
colwidth = 32;
node_height = 72;
0-15: Source Port;
16-31: Destination Port;
32-63: Sequence Number;
64-95: Acknowledgment Number;
96-99: Data Offset;
100-105: Reserved;
106: URG [rotate = 270];
107: ACK [rotate = 270];
108: PSH [rotate = 270];
109: RST [rotate = 270];
110: SYN [rotate = 270];
111: FIN [rotate = 270];
112-127: Window;
128-143: Checksum;
144-159: Urgent Pointer;
160-191: (Options and Padding);
192-223: data [colheight = 3];
}
render the packet diagram image
Simple activity diagram can be annotated like this
actdiag {
write -> convert -> image
lane user {
label = "User"
write [label = "Writing reST"];
image [label = "Get diagram IMAGE"];
}
lane actdiag {
convert [label = "Convert reST to Image"];
}
}
If you're into UML, you may use this annotation format
[Pirate|eyeCount: Int|raid();pillage()|
[beard]--[parrot]
[beard]-:>[foul mouth]
]
[<abstract>Marauder]<:--[Pirate]
[Pirate]- 0..7[mischief]
[jollyness]->[Pirate]
[jollyness]->[rum]
[jollyness]->[singing]
[Pirate]-> *[rum|tastiness: Int|swig()]
[Pirate]->[singing]
[singing]<->[rum]
[<start>st]->[<state>plunder]
[plunder]->[<choice>more loot]
[more loot]->[st]
[more loot] no ->[<end>e]
[<actor>Sailor] - [<usecase>shiver me;timbers]
You may also be a component fan
@startuml
!include C4_Container.puml
LAYOUT_TOP_DOWN
LAYOUT_WITH_LEGEND()
title Container diagram for Internet Banking System
Person(customer, Customer, "A customer of the bank, with personal bank accounts")
System_Boundary(c1, "Internet Banking") {
Container(web_app, "Web Application", "Java, Spring MVC", "Delivers the static content and the Internet banking SPA")
Container(spa, "Single-Page App", "JavaScript, Angular", "Provides all the Internet banking functionality to customers via their web browser")
Container(mobile_app, "Mobile App", "C#, Xamarin", "Provides a limited subset of the Internet banking functionality to customers via their mobile device")
ContainerDb(database, "Database", "SQL Database", "Stores user registration information, hashed auth credentials, access logs, etc.")
Container(backend_api, "API Application", "Java, Docker Container", "Provides Internet banking functionality via API")
}
System_Ext(email_system, "E-Mail System", "The internal Microsoft Exchange system")
System_Ext(banking_system, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
Rel(customer, web_app, "Uses", "HTTPS")
Rel(customer, spa, "Uses", "HTTPS")
Rel(customer, mobile_app, "Uses")
Rel_Neighbor(web_app, spa, "Delivers")
Rel(spa, backend_api, "Uses", "async, JSON/HTTPS")
Rel(mobile_app, backend_api, "Uses", "async, JSON/HTTPS")
Rel_Back_Neighbor(database, backend_api, "Reads from and writes to", "sync, JDBC")
Rel_Back(customer, email_system, "Sends e-mails to")
Rel_Back(email_system, backend_api, "Sends e-mails using", "sync, SMTP")
Rel_Neighbor(backend_api, banking_system, "Uses", "sync/async, XML/HTTPS")
@enduml
I use excalidraw.com diagrams a lot when documenting my code. They stored in a versionable JSON documents, that kroki can render natively. You may use them embedded in the comment or store the JSON document on filesystem and reference it (I use the latter).
It is even possible to generate a nice bar-chart like this (with some obscure JSON syntax ;)