Convert Callouts to Definition Lists
Converts AsciiDoc code blocks with callout-style annotations to a cleaner definition list format with “where:” prefix.
Choosing the Right Tool
This is the batch conversion tool - it processes all code blocks with a single target format. Use this when you want consistent formatting across all files or need to automate conversions in scripts.
For per-code-block control where you choose the format for each individual code block interactively, see convert-callouts-interactive.
Quick Decision Guide:
- Same format for all blocks → Use this tool (
convert-callouts-to-deflist)- Different formats per block → Use
convert-callouts-interactive- Automation/CI pipelines → Use this tool (
convert-callouts-to-deflist)- Editorial review needed → Use
convert-callouts-interactive
Overview
Traditional AsciiDoc callouts use numbered markers (<1>, <2>, etc.) in code blocks with corresponding explanations below. This tool converts them to cleaner formats: definition lists (default), bulleted lists, or inline comments.
Supported Input Formats:
- List format:
<1> Explanation text(traditional callout style) - Multi-column tables: 2-column and 3-column tables with callout explanations (used in some documentation like Debezium)
The tool automatically detects the input format and converts it to your chosen output format.
Important: This tool is designed to assist your conversion efforts. You must:
- Carefully review all content changes before committing them to ensure accuracy and readability
- Pay special attention to merged callouts - when multiple callouts appear on the same line, their explanations are automatically merged (see example below)
- Pay attention to any warnings displayed during processing
- Review the warning summary at the end and address any callout mismatches before re-running
- Test documentation builds after converting to verify correctness
Installation
Install with the doc-utils package:
pipx install rolfedh-doc-utils
Then run directly as a command:
convert-callouts-to-deflist [options] [path]
The tool automatically processes all .adoc files recursively in the specified directory (or current directory by default).
Features
Automatic Format Detection
Automatically detects and processes multiple input formats:
- List format: Traditional
<1> Explanationstyle -
2-column tables: Callout number explanation -
3-column tables: Item number value description (Debezium style) - Conditional statements: Preserves
ifdef::/ifndef::/endif::in table cells
Multiple Output Formats
Convert callouts to your preferred format:
- Definition lists (default): Uses “where:” prefix with AsciiDoc definition lists
- Bulleted lists: Follows Red Hat style guide format
- Inline comments: Embeds explanations as code comments with language-specific syntax
Code Line Extraction
Uses the complete code line as the definition list term:
- Captures the full line of code with callout (stripped of callout marker)
- Preserves all syntax including angle brackets (Java generics, comparison operators, etc.)
- Works correctly with user-replaceable values like
<my-value>and complex expressions likeCrudRepository<MyEntity, Integer>
Smart Comment Handling
For inline comments format:
- Detects programming language from
[source,language]attribute - Uses appropriate comment syntax (40+ languages supported)
- Automatically falls back to definition list if comment exceeds
--max-comment-length(default: 120 characters) - Displays warnings for overly long comments
Validation and Safety
- Validates callout numbers match between code and explanations
- Skips blocks with mismatched numbers and displays warnings
- Preserves legitimate angle brackets (e.g., Java generics like
List<String>) - Non-destructive: Always test with
--dry-runfirst - Automatically excludes
.valedirectory
Advanced Capabilities
- Merged callouts: When multiple callouts appear on same line, automatically merges explanations using AsciiDoc
+continuation - Optional markers: Preserves “Optional.” prefixes from explanations
- Non-sequential callouts: Handles callouts like
<1>,<3>,<5> - Exclusion support: Filter directories and files via CLI options or exclusion list file
Usage
Process All Files in Current Directory (Default)
convert-callouts-to-deflist
Automatically scans all .adoc files recursively in the current directory.
Process Specific Directory
convert-callouts-to-deflist modules/
Process Single File
convert-callouts-to-deflist assemblies/my-guide.adoc
Preview Changes (Dry Run)
convert-callouts-to-deflist --dry-run
Shows what would be changed without modifying files.
Choose Output Format
# Definition list format (default)
convert-callouts-to-deflist modules/
# Bulleted list format
convert-callouts-to-deflist --format bullets modules/
# Inline comments format
convert-callouts-to-deflist --format comments modules/
Exclude Directories
convert-callouts-to-deflist --exclude-dir archive --exclude-dir temp
Excludes specified directories from processing. Note: .vale is excluded by default.
Use Exclusion List File
convert-callouts-to-deflist --exclude-list .docutils-ignore
Example .docutils-ignore file:
# Directories to exclude
.vale/
archive/
temp/
# Specific files
test-file.adoc
Verbose Mode
convert-callouts-to-deflist --verbose
Shows detailed processing information.
Options
path- File or directory to process (default: current directory)-n, --dry-run- Preview changes without modifying files-v, --verbose- Enable detailed logging-f, --format {deflist,bullets,comments}- Output format: “deflist” for definition list with “where:” (default), “bullets” for bulleted list per Red Hat style guide, “comments” for inline comments-s, --specifies- Add “Specifies “ prefix before each definition (only applies to deflist format)--prefix TEXT- Custom prefix to add before each definition (only applies to deflist format, e.g., “Indicates “)--max-comment-length N- Maximum comment length in characters (default: 120). For inline comments format, automatically falls back to definition list if comment exceeds this length--force- USE WITH CAUTION: Force strip callouts from code blocks even when warnings are present. Requires confirmation prompt. Only use after reviewing all warnings in the warnings report and confirming remaining issues are acceptable--exclude-dir DIR- Exclude directory (can be used multiple times)--exclude-file FILE- Exclude file (can be used multiple times)--exclude-list FILE- Load exclusion list from file-h, --help- Show help message
Output Formats
The tool supports three output formats, selectable via the --format option:
Definition List Format (Default)
Uses AsciiDoc definition lists with “where:” prefix. This is the default format.
convert-callouts-to-deflist modules/
Output:
where:
`<my-secret>`::
The secret name
`<my-key>`::
The secret key value
With “Specifies” prefix:
convert-callouts-to-deflist --specifies modules/
# or shorthand:
convert-callouts-to-deflist -s modules/
Output:
where:
`<my-secret>`::
Specifies the secret name
`<my-key>`::
Specifies the secret key value
With custom prefix:
convert-callouts-to-deflist --prefix "Indicates " modules/
Output:
where:
`<my-secret>`::
Indicates the secret name
`<my-key>`::
Indicates the secret key value
The prefix options (
-sand--prefix) only apply to definition list format. They have no effect when using--format bulletsor--format comments.
Bulleted List Format
Uses AsciiDoc bulleted lists following the Red Hat Style Guide format. Best for explaining YAML structures or multi-line code blocks.
convert-callouts-to-deflist --format bullets modules/
Output:
* `<my-secret>`: The secret name
* `<my-key>`: The secret key value
Inline Comments Format
Converts callouts to inline comments within the code itself, using appropriate comment syntax for the code block’s language. This removes the separate explanation section entirely.
convert-callouts-to-deflist --format comments modules/
Output:
[source,yaml]
----
apiVersion: v1
kind: Secret
metadata:
name: <my-secret> # The secret name
data:
key: <my-key> # The secret key value
----
The tool automatically detects the programming language from the [source,language] attribute and uses the appropriate comment syntax:
//for Java, JavaScript, TypeScript, C, C++, Go, Rust, etc.#for Python, Ruby, Bash, YAML, etc.--for SQL, Lua<!--for HTML, XML
Smart Length Handling: If an explanation exceeds --max-comment-length (default: 120 characters), the tool automatically falls back to definition list format for that block and displays a warning.
When to use each format:
- Definition list (
--format deflist): Default choice, works well for most cases, provides semantic “where:” prefix - Bulleted list (
--format bullets): Follows Red Hat style guide, preferred for YAML files and complex configurations - Inline comments (
--format comments): Best for code examples where explanations are brief and fit naturally as comments
All formats support merged callouts, optional markers, and user-replaceable values.
Transformation Examples
Example 1: Callouts with User-Replaceable Values
Before (Callout Style):
[source,yaml]
----
apiVersion: v1
kind: Secret
metadata:
name: <my-secret> <1>
data:
key: <my-key> <2>
----
<1> The secret name
<2> The secret key value
After (Definition List Style):
[source,yaml]
----
apiVersion: v1
kind: Secret
metadata:
name: <my-secret>
data:
key: <my-key>
----
where:
`name: <my-secret>`::
The secret name
`key: <my-key>`::
The secret key value
Example 2: Callouts Explaining Code Lines
Before (Callout Style):
[source,java]
----
httpSecurity
.get("/public/*").permit() <1>
.path("/admin/*").roles("admin") <2>
.path("/forbidden").authorization().deny(); <3>
----
<1> Permits all GET requests to paths matching `/public/*` without authentication.
<2> Restricts access to users with the `admin` role.
<3> Denies all access to the `/forbidden` path.
After (Definition List Style):
[source,java]
----
httpSecurity
.get("/public/*").permit()
.path("/admin/*").roles("admin")
.path("/forbidden").authorization().deny();
----
where:
`.get("/public/*").permit()`::
Permits all GET requests to paths matching `/public/*` without authentication.
`.path("/admin/*").roles("admin")`::
Restricts access to users with the `admin` role.
`.path("/forbidden").authorization().deny();`::
Denies all access to the `/forbidden` path.
Example 3: Multiple Callouts on Same Line (Merged Explanations)
⚠️ REVIEW CAREFULLY: When multiple callouts appear on the same code line, the tool automatically merges their explanations into a single definition list entry using AsciiDoc’s list continuation marker (+). This behavior is correct for most cases, but you should verify that the merged explanations read naturally together.
Before (Callout Style):
[source,java]
----
@Path("hello")
public class HelloResource {
@BasicAuthentication <1> <2>
@Path("basic")
public String basicAuthMechanism() {
return "basic";
}
}
----
<1> Enables basic authentication for this endpoint.
<2> Authentication is required by default when using this annotation.
After (Definition List Style - Merged):
[source,java]
----
@Path("hello")
public class HelloResource {
@BasicAuthentication
@Path("basic")
public String basicAuthMechanism() {
return "basic";
}
}
----
where:
`@BasicAuthentication`::
Enables basic authentication for this endpoint.
+
Authentication is required by default when using this annotation.
Why This Matters:
- Both callouts reference the same code element (
@BasicAuthentication) - Their explanations are semantically related (the first explains what it does, the second adds important context)
- The merged format is cleaner and avoids duplicate terms in the definition list
- The
+continuation marker properly connects the related explanations
When to Review:
- If the merged explanations feel redundant or repetitive
- If the explanations would read better as separate items (consider refactoring the callouts in the source)
- If the order of explanations affects understanding (they merge in callout number order)
Example 4: Multi-Column Table Format (Automatic Detection)
The tool automatically detects and converts multi-column table format callout explanations used in some documentation (e.g., Debezium).
2-Column Table Format (Callout | Explanation)
Before (2-Column Table):
[source,sql]
----
ALTER TABLE inventory ADD COLUMN c1 INT; <1>
INSERT INTO myschema.inventory (id,c1) VALUES (100, 1); <2>
----
[cols="1,3"]
|===
|<1>
|Adds a new column to the inventory table.
|<2>
|Inserts a sample record with the new column value.
|===
After (Definition List Style):
[source,sql]
----
ALTER TABLE inventory ADD COLUMN c1 INT;
INSERT INTO myschema.inventory (id,c1) VALUES (100, 1);
----
where:
`ALTER TABLE inventory ADD COLUMN c1 INT;`::
Adds a new column to the inventory table.
`INSERT INTO myschema.inventory (id,c1) VALUES (100, 1);`::
Inserts a sample record with the new column value.
3-Column Table Format (Debezium Style)
| Some documentation uses a 3-column format: item number | value | description. The tool detects this format and combines the value and description. |
Before (3-Column Table):
[source,sql]
----
INSERT INTO myschema.debezium_signal (id, type, data) // <1>
values ('ad-hoc-1', // <2>
'execute-snapshot', // <3>
'{"data-collections": ["schema1.table1"]}'); // <4>
----
.Descriptions of fields in a SQL command
[cols="1,2,6",options="header"]
|===
|Item |Value |Description
|1
|`myschema.debezium_signal`
|Specifies the fully-qualified name of the signaling table on the source database.
|2
|`ad-hoc-1`
|The `id` parameter specifies an arbitrary string that is assigned as the identifier for the signal request.
|3
|`execute-snapshot`
|The `type` parameter specifies the operation that the signal is intended to trigger.
|4
|`data-collections`
|A required component of the `data` field that specifies an array of table names.
|===
After (Definition List Style):
[source,sql]
----
INSERT INTO myschema.debezium_signal (id, type, data)
values ('ad-hoc-1',
'execute-snapshot',
'{"data-collections": ["schema1.table1"]}');
----
where:
`myschema.debezium_signal`::
Refers to `myschema.debezium_signal`.
Specifies the fully-qualified name of the signaling table on the source database.
`ad-hoc-1`::
Refers to `ad-hoc-1`.
The `id` parameter specifies an arbitrary string that is assigned as the identifier for the signal request.
`execute-snapshot`::
Refers to `execute-snapshot`.
The `type` parameter specifies the operation that the signal is intended to trigger.
`data-collections`::
Refers to `data-collections`.
A required component of the `data` field that specifies an array of table names.
Detection Details:
- Automatic detection: list format, 2-column tables, or 3-column tables (no flags needed)
- For 3-column tables: Combines value and description columns
- Preserves conditional statements (
ifdef::/ifndef::/endif::) in table cells - Skips header rows automatically
Edge Cases and Warnings
Callout Mismatch Warnings
When the tool detects a mismatch between callout numbers in code and their explanations, it displays a warning immediately and skips that code block:
WARNING: my-file.adoc lines 141-145: Callout mismatch: code has [1, 2], explanations have [1, 3]
Duplicate Callout Detection: The warning now shows duplicate callout numbers in explanation tables:
WARNING: file.adoc lines 55-72: Callout mismatch: code has [1, 2, 3, 4, 5, 6, 7, 8, 9], explanations have [1, 2, 3, 4, 5, 7, 8, 8, 9]
In this example, the table has two rows with callout 8 and is missing callout 6.
This prevents incorrect conversions when:
- Callout numbers are non-consecutive in the code but explanations use different numbers
- An explanation is missing for a callout in the code
- An explanation exists for a callout that’s not in the code
- A table has duplicate callout numbers (documentation error)
Missing Explanations Warning
When the tool finds a code block with callouts but no explanations after it, it displays a warning:
WARNING: file.adoc line 211: Code block has callouts [1, 2, 3, 4] but no explanations found after it.
This may indicate: explanations are shared with another code block, explanations are in an unexpected
location, or documentation error (missing explanations). Consider reviewing this block manually.
This commonly occurs when:
- Shared explanations: Multiple code blocks share the same explanation table (e.g., in conditional sections like
ifdef::community[]andifdef::product[]) - Unexpected location: Explanations are not immediately after the code block
- Documentation error: Explanations are genuinely missing
Action Required: Review these blocks manually to determine the appropriate handling.
Warnings Report File
By default, the tool generates a detailed warnings report file in AsciiDoc format for easy navigation and review.
Console output (minimal):
Processed 15 AsciiDoc file(s)
Modified 8 file(s) with 32 code block conversion(s)
⚠️ 4 Warning(s) - See callout-warnings-report.adoc for details
The generated callout-warnings-report.adoc file contains:
- Summary: Total warnings count by type
- Callout Mismatch Warnings: Detailed analysis showing duplicates, missing callouts, and off-by-one errors
- Missing Explanations Warnings: Lists blocks that may have shared explanations or documentation errors
- Structured AsciiDoc format: Easy to navigate with table of contents, can be opened in any text editor or rendered
Example report structure:
= Callout Conversion Warnings Report
:toc:
Generated: 2025-10-22 15:33:15
== Summary
Total warnings: 4
- Callout mismatches: 2
- Missing explanations: 2
== Callout Mismatch Warnings
=== mongodb.adoc
*Lines 766-901*
Code has:: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Explanations have:: [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10]
Issues detected::
- Duplicate callout: 9 (appears 2 times in explanations)
- Missing callout: 11 (in code but not in explanations)
== Missing Explanations Warnings
=== oracle.adoc
*Line 2772*
Callouts in code:: [1, 2, 3, 4, 5, ..., 25]
Possible causes::
- Explanations shared with another code block (e.g., in conditional sections)
- Explanations in unexpected location
- Documentation error (missing explanations)
Action:: Review this block manually
Command-line options:
# Enable report generation (default)
convert-callouts-to-deflist --warnings-report modules/
# Disable report generation (legacy console-only mode)
convert-callouts-to-deflist --no-warnings-report modules/
# Custom report file path
convert-callouts-to-deflist --warnings-file my-warnings.adoc modules/
Benefits:
- Clean console output without warning spam
- Structured warnings that are easy to review and share
- Can be committed to git to track warning resolution progress
- AsciiDoc format renders nicely in editors and GitHub
Force Mode
CAUTION: Use force mode sparingly and only after thorough review.
After reviewing the warnings report and manually fixing callout issues where appropriate, you may have remaining warnings that are acceptable (e.g., intentionally shared explanations between code blocks). In such cases, you can use the --force option to strip callouts despite warnings:
convert-callouts-to-deflist --force modules/
Before proceeding, you will see a confirmation prompt:
⚠️ FORCE MODE ENABLED ⚠️
This will strip callouts from code blocks even when warnings are present.
You should only use this option AFTER:
1. Reviewing all warnings in the warnings report
2. Manually fixing callout issues where appropriate
3. Confirming that remaining warnings are acceptable
Are you sure you want to proceed with force mode? (yes/no):
What force mode does:
- Missing explanations: Strips callouts from code without creating explanation lists
- Callout mismatches: Converts blocks using available explanations (may leave some callouts unexplained)
- Requires confirmation: Prompts before proceeding (skipped in
--dry-runmode)
Recommended workflow:
- Run without
--forcefirst:convert-callouts-to-deflist modules/ - Review warnings report:
callout-warnings-report.adoc - Fix genuine issues (duplicate callouts, missing explanations, etc.)
- Rerun conversion to verify fixes
- Only use
--forcefor remaining acceptable warnings - Always review with
git diffbefore committing
Example scenario where force mode is appropriate:
ifdef::product[]
[source,yaml]
----
config:
value: <my-value> <1>
----
<1> Product-specific explanation
endif::[]
ifdef::community[]
[source,yaml]
----
config:
value: <my-value> <1> # Shares callout <1> with product block
----
endif::[]
In this case, the second code block shares explanations with the first. After confirming this is intentional, you can use --force to strip the callouts from both blocks.
Real-World Example
Complex Secret Configuration
Before:
[source,yaml]
----
cat <<EOF | oc -n {my-product-namespace} create -f -
apiVersion: v1
kind: Secret
metadata:
name: <my-product-database-certificates-secrets> <1>
type: Opaque
stringData:
postgres-ca.pem: |-
-----BEGIN CERTIFICATE-----
<ca-certificate-key> <2>
postgres-key.key: |-
-----BEGIN CERTIFICATE-----
<tls-private-key> <3>
postgres-crt.pem: |-
-----BEGIN CERTIFICATE-----
<tls-certificate-key> <4>
EOF
----
<1> Specifies the name of the certificate secret.
<2> Specifies the CA certificate key.
<3> Optional. Specifies the TLS private key.
<4> Optional. Specifies the TLS certificate key.
After:
[source,yaml]
----
cat <<EOF | oc -n {my-product-namespace} create -f -
apiVersion: v1
kind: Secret
metadata:
name: <my-product-database-certificates-secrets>
type: Opaque
stringData:
postgres-ca.pem: |-
-----BEGIN CERTIFICATE-----
<ca-certificate-key>
postgres-key.key: |-
-----BEGIN CERTIFICATE-----
<tls-private-key>
postgres-crt.pem: |-
-----BEGIN CERTIFICATE-----
<tls-certificate-key>
EOF
----
where:
`name: <my-product-database-certificates-secrets>`::
Specifies the name of the certificate secret.
`<ca-certificate-key>`::
Specifies the CA certificate key.
`<tls-private-key>`::
Optional. Specifies the TLS private key.
`<tls-certificate-key>`::
Optional. Specifies the TLS certificate key.
Technical Details
This tool uses the callout_lib Python library for callout detection and conversion. See the library README for detailed implementation information.
Key Processing Steps:
- Scan for code blocks with
[source]attributes - Extract callout numbers from code and group by line
- Detect and extract explanations (list format or table format)
- Validate callout numbers match between code and explanations
- Convert to chosen output format (deflist, bullets, or comments)
- Replace callout sections in document
Supported Comment Syntax (for inline comments format):
- C-style
//: Java, JavaScript, TypeScript, C, C++, Go, Rust, Swift, Kotlin, etc. - Hash
#: Python, Ruby, Bash, YAML, Shell, Perl, R, etc. - SQL
--: SQL, PL/SQL, T-SQL, Lua - HTML/XML
<!--: HTML, XML, SVG - Others: Lisp
;, MATLAB/LaTeX%, etc. (40+ languages total)
Best Practices
- Always work in a git branch before converting files
- Use
--dry-runfirst to preview what will be changed - Choose the appropriate format:
- Use
--format deflist(default) for general documentation - Use
--format bulletsfor YAML files and complex configurations (follows Red Hat style guide)
- Use
- Review changes with
git diffbefore committing - Test documentation builds after converting
- Start with a small batch to verify behavior
Example Workflow
# Create a feature branch
git checkout -b convert-callouts
# Preview changes on entire repository (.vale is excluded by default)
cd ~/rhbquarkus
convert-callouts-to-deflist --dry-run
# Example output:
# Found 292 AsciiDoc file(s) to process
# Would modify: asciidoc/logging.adoc (3 code block(s))
# Would modify: asciidoc/security-jwt.adoc (3 code block(s))
# ...
# Would modify 19 file(s) with 104 code block conversion(s)
# Process a specific directory
convert-callouts-to-deflist --dry-run asciidoc/
# Convert files
convert-callouts-to-deflist
# Review changes
git diff
# Test build
./scripts/build-docs.sh
# Commit changes
git add .
git commit -m "Convert callouts to definition list format"
# Or use bulleted list format for YAML files
convert-callouts-to-deflist --format bullets --dry-run deployment-guides/
convert-callouts-to-deflist --format bullets deployment-guides/
git commit -m "Convert YAML callouts to bulleted list format"
See the main Tools Reference for more documentation utilities.