Skip to content

Commit 3e32303

Browse files
authored
Merge pull request #21 from mariotoffia/feature/multi-module
support for go.work and sub-modules
2 parents 7fa4d0b + 66ff117 commit 3e32303

33 files changed

+5091
-127
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ docs.html
2121
# other junk files
2222
.temp-files/
2323
.claude/
24+
.cache/
2425
goasciidoc

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"colwidth",
4747
"COMT",
4848
"consts",
49+
"coremodel",
4950
"dataductus",
5051
"datapoints",
5152
"debugf",
@@ -78,6 +79,7 @@
7879
"kvack",
7980
"mariotoffia",
8081
"martoffi",
82+
"metacharacter",
8183
"modfile",
8284
"mydoc",
8385
"mypkg",
@@ -103,7 +105,9 @@
103105
"tbaz",
104106
"templatedir",
105107
"testcases",
108+
"texttemplate",
106109
"tmpdir",
110+
"tmpl",
107111
"toclevel",
108112
"toclevels",
109113
"Toffia",

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ test:
44
@go test -v ./... -cover
55

66
docs-goasciidoc:
7-
@go run main.go --type-links external -i -t --highlighter goasciidoc --render struct-json --render struct-yaml\
7+
@go run main.go --exclude='glb:**/.temp-files/**' --type-links external -i -t --highlighter goasciidoc --render struct-json --render struct-yaml\
88
-c "{\"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\": 3}"
99
docs:
10-
@go run main.go --type-links external -i -t \
10+
@go run main.go --exclude='glb:**/.temp-files/**' --type-links external -i -t \
1111
-c "{\"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\": 3}"
1212
golden:
1313
@UPDATE_GOLDEN=1 go test ./asciidoc -run TestProducerGenerateGolden

README.md

Lines changed: 260 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- **🎨 Rich Diagrams** - Embed sequence diagrams, UML, flowcharts, and more directly in your code comments
1515
- **🔗 Smart Linking** - Automatic cross-references between types, both internal and external (to pkg.go.dev)
1616
- **📊 Visual Examples** - Render structs as JSON/YAML examples automatically
17-
- **✨ Syntax Highlighting** - Beautiful code highlighting with clickable type references
17+
- **✨ Syntax Highlighting** - Code highlighting with clickable type references
1818
- **🎯 Flexible Templates** - Customize every aspect of your documentation output
1919

2020
(see asciidoc [markup](https://asciidoctor.org/docs/asciidoc-writers-guide/) guide)
@@ -29,7 +29,7 @@ go install github.com/mariotoffia/goasciidoc@latest
2929
goasciidoc -o docs.adoc --type-links external --highlighter goasciidoc
3030
```
3131

32-
That's it! You now have beautiful documentation with clickable type links and syntax highlighting.
32+
That's it! You now have documentation with clickable type links and syntax highlighting.
3333

3434

3535
### Customization
@@ -47,6 +47,8 @@ The above will generate standard code documentation, internal and test is exclud
4747

4848
It also resolves both internal and external type references and make clickable asciidoc links to those types. When _highlighter_ is set to `goasciidoc` even the function signatures are nicely highlighted with links to referenced types (otherwise those are standar source, go blocks with no links).
4949

50+
Need to skip generated or scratch folders? Add one or more `--exclude` filters. Use regexes or the `glb:` shorthand, e.g. `--exclude 'glb:**/.temp-files/**'` (_glb:_ tries to translate _glob_ expression to _regex_).
51+
5052
It also will render structs as JSON (example) when `--render struct-json` is set. Supported renderers are:
5153

5254
- `struct-json`: Renders structs as JSON
@@ -111,8 +113,8 @@ You may now use the `goasciidoc` e.g. in the `goasciidoc` repo by `goasciidoc --
111113

112114

113115
```bash
114-
goasciidoc v0.5.0
115-
Usage: goasciidoc [--out PATH] [--stdout] [--debug] [--module PATH] [--internal] [--private] [--nonexported] [--test] [--noindex] [--notoc] [--indexconfig JSON] [--overrides OVERRIDES] [--list-template] [--out-template OUT-TEMPLATE] [--packagedoc FILEPATH] [--templatedir TEMPLATEDIR] [--type-links MODE] [PATH [PATH ...]] --highlighter NAME
116+
goasciidoc v0.6.0
117+
Usage: goasciidoc [--out PATH] [--stdout] [--debug] [--module PATH] [--internal] [--private] [--nonexported] [--test] [--noindex] [--notoc] [--indexconfig JSON] [--overrides OVERRIDES] [--list-template] [--out-template OUT-TEMPLATE] [--packagedoc FILEPATH] [--templatedir TEMPLATEDIR] [--type-links MODE] [--sub-module MODE] [--package-mode MODE] [PATH [PATH ...]] --highlighter NAME
116118

117119
Positional arguments:
118120
PATH Directory or files to be included in scan (if none, current path is used)
@@ -141,6 +143,8 @@ Options:
141143
--templatedir TEMPLATEDIR
142144
Loads template files *.gtpl from a directory, use --list to get valid names of templates
143145
--type-links MODE Controls type reference linking: disabled, internal, or external (default disabled)
146+
--sub-module MODE Submodule processing mode: none, single, or separate (default none)
147+
--package-mode MODE Package-level rendering mode: none, include, or link (default none)
144148
--highlighter NAME Source code highlighter to use; available: none, goasciidoc
145149
--help, -h display this help and exit
146150
--version display version and exit
@@ -150,6 +154,258 @@ Options:
150154
151155
When generating documentation, `goasciidoc` can now render hyperlinks for referenced Go types. Enable it with `--type-links internal` to link across types within the current module, or `--type-links external` to also point at [`pkg.go.dev`](https://pkg.go.dev/) for external packages. By default (`--type-links disabled`) type names are rendered as plain text, preserving the behaviour of earlier releases.
152156
157+
### Multi-Module & Workspace Support
158+
159+
`goasciidoc` fully supports Go workspaces and multi-module projects! It automatically discovers `go.work` files or recursively finds multiple `go.mod` files, giving you flexible documentation generation options.
160+
161+
#### Smart Discovery
162+
163+
`goasciidoc` intelligently discovers your project structure:
164+
165+
1. **Walks up** from the current directory (or `--module` path)
166+
2. **Prefers `go.work`** over `go.mod` at each directory level
167+
3. **Auto-discovers** all modules in your workspace
168+
169+
No configuration needed—it just works!
170+
171+
#### Generation Modes
172+
173+
Control how multi-module documentation is generated with the `--sub-module` flag:
174+
175+
##### None (Default)
176+
```bash
177+
goasciidoc
178+
```
179+
Original behavior—documents a single module only. Backward compatible with all existing projects.
180+
181+
##### Single Mode (Merged)
182+
```bash
183+
goasciidoc --sub-module=single -o docs.adoc
184+
```
185+
Merges all workspace modules into **one unified document**. Perfect for:
186+
- Getting a complete overview of your workspace
187+
- Creating comprehensive API documentation
188+
- Generating a single file for distribution
189+
190+
**Output:** `docs.adoc` (contains all modules with clear section breaks)
191+
192+
##### Separate Mode (Per-Module Files)
193+
```bash
194+
goasciidoc --sub-module=separate -o api-docs
195+
```
196+
Generates **separate documentation files** for each module. Ideal for:
197+
- Large workspaces with independent modules
198+
- Modular documentation that can be distributed separately
199+
- Projects where each module has distinct audiences
200+
201+
**Output:** `api-docs-module1.adoc`, `api-docs-module2.adoc`, etc.
202+
203+
#### Cross-Module Type Linking
204+
205+
When using `--type-links internal` with multi-module projects, types automatically reference each other:
206+
207+
```bash
208+
# Single mode: Uses anchor links within the same document
209+
goasciidoc --sub-module=single --type-links=internal
210+
211+
# Separate mode: Uses file links between documents
212+
goasciidoc --sub-module=separate --type-links=internal
213+
```
214+
215+
**Example:** If `module2` uses a type from `module1`:
216+
- **Single mode:** Generates `<<module1-Service,Service>>` (anchor link)
217+
- **Separate mode:** Generates `link:module1.adoc#module1-Service[Service]` (file link)
218+
219+
#### Workspace Examples
220+
221+
**Example 1: Go Workspace**
222+
```
223+
myproject/
224+
├── go.work ← Automatically discovered!
225+
├── go.mod
226+
├── main.go
227+
└── modules/
228+
├── moduleA/
229+
│ ├── go.mod
230+
│ └── service.go
231+
└── moduleB/
232+
├── go.mod
233+
└── handler.go
234+
```
235+
236+
```bash
237+
cd myproject
238+
goasciidoc --sub-module=single -o workspace-docs.adoc
239+
# Documents all 3 modules in one file
240+
```
241+
242+
**Example 2: Monorepo Without go.work**
243+
```
244+
monorepo/
245+
├── service-a/
246+
│ └── go.mod
247+
├── service-b/
248+
│ └── go.mod
249+
└── service-c/
250+
└── go.mod
251+
```
252+
253+
```bash
254+
cd monorepo
255+
goasciidoc --sub-module=separate -o services
256+
# Generates: services-service-a.adoc, services-service-b.adoc, services-service-c.adoc
257+
```
258+
259+
**Example 3: Complete Multi-Module Documentation**
260+
```bash
261+
# Generate comprehensive docs with all features enabled
262+
goasciidoc \
263+
--sub-module=single \
264+
--type-links=internal \
265+
--highlighter=goasciidoc \
266+
--render struct-json \
267+
--render struct-yaml \
268+
-o complete-api-docs.adoc
269+
270+
# Result: Fully-linked documentation of your entire workspace!
271+
```
272+
273+
#### Module Headers
274+
275+
Each module section automatically includes:
276+
- **Module name** (e.g., `github.com/myorg/myproject/moduleA`)
277+
- **Go version** requirement
278+
- **Location** on the filesystem
279+
280+
This makes it easy to understand the structure of complex workspaces.
281+
282+
### Package-Level Rendering
283+
284+
`goasciidoc` can generate separate documentation files for each package, creating a modular, navigable documentation structure. This is perfect for large projects where you want each package to have its own standalone documentation file.
285+
286+
#### Package Modes
287+
288+
Control package-level documentation with the `--package-mode` flag:
289+
290+
##### None (Default)
291+
```bash
292+
goasciidoc
293+
```
294+
Standard behavior—generates a single documentation file with all packages inline. Compatible with existing workflows.
295+
296+
##### Include Mode (Package per File + Master Index)
297+
```bash
298+
goasciidoc --package-mode=include -o docs/index.adoc
299+
```
300+
Creates **separate files for each package** with a master index that uses AsciiDoc `include::` directives. Perfect for:
301+
- Large projects with many packages
302+
- Modular documentation that's easy to navigate
303+
- Build systems that process includes at render time
304+
305+
**Output:**
306+
```
307+
docs/
308+
├── index.adoc # Master index with includes
309+
└── packages/
310+
├── github.com_myorg_project.adoc
311+
├── github.com_myorg_project_api.adoc
312+
├── github.com_myorg_project_models.adoc
313+
└── github.com_myorg_project_utils.adoc
314+
```
315+
316+
**Master index** uses include directives:
317+
```asciidoc
318+
= My Project - Package Documentation
319+
include::packages/github.com_myorg_project.adoc[]
320+
include::packages/github.com_myorg_project_api.adoc[]
321+
```
322+
323+
##### Link Mode (Independent Package Files)
324+
```bash
325+
goasciidoc --package-mode=link -o docs/index.adoc
326+
```
327+
Creates **independent documentation files** for each package with a master index that **links** to them. Ideal for:
328+
- Documentation hosted on web servers
329+
- Projects where packages should be viewable independently
330+
- Static site generators that don't process includes
331+
332+
**Output:**
333+
```
334+
docs/
335+
├── index.adoc # Master index with links
336+
└── packages/
337+
├── github.com_myorg_project.adoc # Standalone document
338+
├── github.com_myorg_project_api.adoc # Standalone document
339+
├── github.com_myorg_project_models.adoc # Standalone document
340+
└── github.com_myorg_project_utils.adoc # Standalone document
341+
```
342+
343+
**Master index** uses links:
344+
```asciidoc
345+
= My Project - Package Documentation
346+
347+
== Package: github.com/myorg/project
348+
link:packages/github.com_myorg_project.adoc[View full documentation]
349+
350+
== Package: github.com/myorg/project/api
351+
link:packages/github.com_myorg_project_api.adoc[View full documentation]
352+
```
353+
354+
#### Package Documentation Features
355+
356+
Each package file is **self-sufficient** with:
357+
- **Document header** with title, TOC, and metadata
358+
- **Level 1 heading** for the package name
359+
- **Complete package contents** (imports, types, functions, etc.)
360+
- **Package references section** showing dependencies
361+
362+
**Package References Section Example:**
363+
```asciidoc
364+
== Package References
365+
366+
=== Internal Packages
367+
This package references the following packages within this project:
368+
369+
* <<pkg-2,github.com/myorg/project/models>> - link:models.adoc[Documentation]
370+
* <<pkg-3,github.com/myorg/project/utils>> - link:utils.adoc[Documentation]
371+
372+
=== External Packages
373+
This package imports the following external packages:
374+
375+
* `github.com/gin-gonic/gin`
376+
* `github.com/spf13/cobra`
377+
```
378+
379+
#### Combining with Multi-Module Support
380+
381+
You can combine package-level and module-level rendering:
382+
383+
```bash
384+
# Separate files per package across multiple modules
385+
goasciidoc --sub-module=single --package-mode=include -o docs/api.adoc
386+
```
387+
388+
This creates package-level documentation for **all packages** across **all modules** in your workspace!
389+
390+
#### Real-World Example
391+
392+
```bash
393+
# Complete package-level documentation with all features
394+
goasciidoc \
395+
--package-mode=include \
396+
--type-links=internal \
397+
--highlighter=goasciidoc \
398+
--render struct-json \
399+
-o docs/api-index.adoc
400+
401+
# Result:
402+
# - docs/api-index.adoc (master index)
403+
# - docs/packages/*.adoc (one file per package)
404+
# - Cross-referenced types between packages
405+
# - Syntax highlighting
406+
# - JSON struct examples
407+
```
408+
153409
## Overriding Default Package Overview
154410
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).
155411

asciidoc/links.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ func (t *TemplateContext) isInternalImport(path string) bool {
243243
if t.File != nil && t.File.Module != nil && strings.HasPrefix(path, t.File.Module.Name) {
244244
return true
245245
}
246+
// Check workspace modules for cross-module references
247+
if t.Workspace != nil && t.Workspace.ContainsModule(path) {
248+
return true
249+
}
250+
return false
251+
}
252+
253+
// isWorkspaceImport checks if import path is in workspace (but not current module)
254+
func (t *TemplateContext) isWorkspaceImport(path string) bool {
255+
if path == "" || t.Workspace == nil {
256+
return false
257+
}
258+
259+
// Check if it's a workspace module but not the current module
260+
if t.Workspace.ContainsModule(path) {
261+
// Not current module
262+
if t.Module == nil || !strings.HasPrefix(path, t.Module.Name) {
263+
return true
264+
}
265+
}
246266
return false
247267
}
248268

@@ -402,6 +422,16 @@ func (t *TemplateContext) linkIdentifier(
402422
if t.isInternalImport(importPath) {
403423
anchor := anchorID(importPath, typeName)
404424
if t.Config != nil && t.Config.TypeLinks != TypeLinksDisabled {
425+
// Check if this is a cross-module reference in separate mode
426+
if t.Config.SubModuleMode == SubModuleSeparate && t.isWorkspaceImport(importPath) {
427+
// Cross-module reference - use file link
428+
targetModule := t.Workspace.ModuleForPath(importPath)
429+
if targetModule != nil {
430+
shortName := goparser.ModuleShortName(targetModule)
431+
return prefix + fmt.Sprintf("link:%s.adoc#%s[%s]", shortName, anchor, trimmed)
432+
}
433+
}
434+
// Same module or single/merged mode - use anchor link
405435
return prefix + fmt.Sprintf("<<%s,%s>>", anchor, trimmed)
406436
}
407437
return prefix + trimmed

0 commit comments

Comments
 (0)