# Clone the repository
git clone https://github.com/docToolchain/asciidoc-linter.git
# Navigate to the project directory
cd asciidoc-linter
# Install the package
pip install .
AsciiDoc Linter Manual
Introduction
About the Project
AsciiDoc Linter is a Python-based tool designed to help maintain high-quality AsciiDoc documentation. It checks your AsciiDoc files for common issues and style violations, helping teams maintain consistent documentation standards.
As part of the docToolchain project (https://doctoolchain.org), it integrates well with existing documentation workflows.
Key Features
-
Heading structure validation
-
Proper hierarchy (no skipped levels)
-
Consistent formatting
-
Single top-level heading
-
-
Block validation
-
Proper block termination
-
Consistent spacing
-
-
Whitespace consistency
-
Line spacing
-
List formatting
-
Tab detection
-
-
Image validation
-
Attribute checking
-
File existence verification
-
-
Multiple output formats (console, JSON, HTML)
Project Goals
-
Improve documentation quality through automated checks
-
Enforce consistent styling across documentation
-
Reduce manual review effort
-
Catch common mistakes early in the documentation process
-
Support documentation as code practices
-
Integrate with existing documentation toolchains
Technical Overview
Component | Description |
---|---|
Language |
Python 3.8+ |
Testing |
unittest framework |
Documentation |
AsciiDoc |
Configuration |
YAML/JSON (planned) |
Getting Started
Prerequisites
-
Python 3.8 or higher
-
Git (for installation)
Installation
Direct installation via pip is planned for future releases. Currently, installation is done via git clone. |
Basic Usage
# Check a single file
asciidoc-lint document.adoc
# Check multiple files
asciidoc-lint doc1.adoc doc2.adoc
# Get help
asciidoc-lint --help
Current Status
Implemented Features
-
Core linting engine
-
Basic rule set (headings, blocks, whitespace, images)
-
Command-line interface
-
Multiple output formats
Planned Features
-
Configuration system (YAML/JSON)
-
Additional rule sets (tables, links, cross-references)
-
Direct installation via pip
-
IDE integration
-
Git pre-commit hooks
Development Guide
Setting Up Development Environment
Clone the Repository
git clone https://github.com/docToolchain/asciidoc-linter.git
cd asciidoc-linter
Create Virtual Environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -e .
Project Structure
asciidoc-linter/
├── asciidoc_linter/ # Main package
│ ├── __init__.py
│ ├── cli.py # Command line interface
│ ├── rules/ # Rule implementations
│ │ ├── __init__.py
│ │ ├── base.py # Base classes for rules
│ │ ├── heading_rules.py
│ │ ├── block_rules.py
│ │ ├── whitespace_rules.py
│ │ └── image_rules.py
│ ├── parser.py # AsciiDoc parser
│ └── reporter.py # Output formatters
├── tests/ # Test files
│ └── rules/ # Rule-specific tests
├── docs/ # Documentation
└── README.adoc
Current Implementation Status
Implemented Features
-
Core rule engine with base classes
-
Rule implementations:
-
Heading rules (hierarchy, format, multiple top-level)
-
Block rules (termination, spacing)
-
Whitespace rules (spacing, formatting)
-
Image rules (attributes, file verification)
-
-
Basic command line interface
-
Multiple output formats (console, JSON, HTML)
-
Comprehensive test suite
Planned Features
-
Configuration system (YAML/JSON)
-
Rule enabling/disabling
-
Severity customization
-
Custom rule parameters
-
-
Additional rule types
-
IDE integration
-
Git hooks
Adding New Rules
Rule Implementation Steps
-
Create a new rule class:
from .base import Rule, Finding, Severity, Position class MyNewRule(Rule): """ RULE_ID: Short description. Detailed explanation of what the rule checks. """ def __init__(self): super().__init__() self.id = "RULE_ID" @property def description(self) -> str: return "Description of what this rule checks" def check(self, content: str) -> List[Finding]: findings = [] # Implementation here return findings
-
Add tests for the rule:
class TestMyNewRule(unittest.TestCase): def setUp(self): self.rule = MyNewRule() def test_valid_case(self): content = "Valid content" findings = self.rule.check(content) self.assertEqual(len(findings), 0) def test_invalid_case(self): content = "Invalid content" findings = self.rule.check(content) self.assertEqual(len(findings), 1) self.assertEqual(findings[0].rule_id, "RULE_ID")
-
Register the rule in the linter
-
Update documentation
Rule Guidelines
-
Clear rule IDs and descriptions
-
Meaningful error messages
-
Proper severity levels
-
Contextual information in findings
-
Comprehensive test cases
-
Documentation with examples
Code Style
Python Guidelines
-
Follow PEP 8
-
Use type hints
-
Write docstrings (Google style)
-
Keep functions focused and testable
-
Maximum line length: 100 characters
-
Use meaningful variable names
Documentation Guidelines
-
Use AsciiDoc format
-
Include examples for all features
-
Explain error messages
-
Document configuration options
-
Keep README.adoc up to date
Testing
Running Tests
# Run all tests
python run_tests.py
# Run specific test file
python -m unittest tests/rules/test_heading_rules.py
# Run specific test case
python -m unittest tests.rules.test_heading_rules.TestHeadingHierarchyRule
Test Guidelines
-
Write tests for all new features
-
Include both positive and negative test cases
-
Test edge cases
-
Maintain high test coverage
-
Use meaningful test names
Pull Request Process
-
Create feature branch
-
Implement changes
-
Add/update tests
-
Update documentation
-
Run full test suite
-
Submit PR
Release Process
-
Update version number in init.py
-
Update changelog
-
Run full test suite
-
Create release notes
-
Tag release
-
Build and publish
Getting Help
-
GitHub Issues: https://github.com/docToolchain/asciidoc-linter/issues
-
Project Wiki: https://github.com/docToolchain/asciidoc-linter/wiki
-
docToolchain Community: https://doctoolchain.org/community
Testing Guide
Overview
The AsciiDoc Linter uses Python’s unittest framework for testing. Tests are organized by rule type and functionality.
Running Tests
Running All Tests
# From project root
python run_tests.py
# With coverage report
coverage run -m unittest discover
coverage report
Running Specific Tests
# Run tests for heading rules
python -m unittest tests/rules/test_heading_rules.py
# Run a specific test class
python -m unittest tests.rules.test_heading_rules.TestHeadingFormatRule
# Run a specific test method
python -m unittest tests.rules.test_heading_rules.TestHeadingFormatRule.test_valid_format
Test Structure
Test Organization
tests/
├── __init__.py
├── rules/
│ ├── __init__.py
│ ├── test_heading_rules.py
│ └── test_block_rules.py
├── test_cli.py
└── test_parser.py
Test Classes
Each rule has its own test class:
class TestHeadingFormatRule(unittest.TestCase):
def setUp(self):
self.rule = HeadingFormatRule()
def test_valid_format(self):
content = """
= Valid Heading
== Another Valid
"""
findings = self.rule.check(content)
self.assertEqual(len(findings), 0)
Writing Tests
Test Guidelines
-
Test both valid and invalid cases
-
Include edge cases
-
Test error messages
-
Test severity levels
-
Test rule configurations
Example Test Pattern
-
Arrange: Set up test data
-
Act: Execute the code
-
Assert: Verify results
def test_invalid_format(self):
# Arrange
content = "=invalid heading"
# Act
findings = self.rule.check(content)
# Assert
self.assertEqual(len(findings), 2)
self.assertEqual(findings[0].severity, Severity.ERROR)
Test Data
Sample Documents
-
Create realistic test documents
-
Cover various scenarios
-
Include complex cases
-
Document test case purpose
Test Fixtures
-
Use setUp and tearDown
-
Share common test data
-
Clean up after tests
Continuous Integration
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Tests
run: python run_tests.py
Coverage Requirements
-
Aim for 90%+ coverage
-
Cover all code paths
-
Include error conditions
-
Test edge cases
Usage Guide
Command Line Interface
Basic Usage
# Check a single file
asciidoc-lint document.adoc
# Check multiple files
asciidoc-lint doc1.adoc doc2.adoc
# Check with specific output format
asciidoc-lint --format json document.adoc
# Use specific configuration
asciidoc-lint --config my-config.yml document.adoc
Command Line Options
Option | Default | Description |
---|---|---|
--format |
console |
Output format (console, json, html) |
--config |
None |
Path to configuration file |
--verbose |
False |
Enable verbose output |
--quiet |
False |
Suppress non-error output |
Configuration
Configuration File
# .asciidoc-lint.yml
rules:
HEAD001:
enabled: true
severity: error
HEAD002:
enabled: true
severity: warning
Rule Configuration
-
Enable/disable rules
-
Set severity levels
-
Configure rule-specific options
-
Set file patterns
Output Formats
Console Output
document.adoc:15 ERROR: Heading level skipped
document.adoc:23 WARNING: Heading should start with uppercase
JSON Output
{
"findings": [
{
"rule": "HEAD001",
"severity": "error",
"message": "Heading level skipped",
"line": 15
}
]
}
HTML Report
Generates a detailed HTML report with:
-
Summary statistics
-
Detailed findings
-
Source context
-
Rule explanations
Integration
Git Pre-commit Hook
#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACM | grep '.adoc$')
if [ -n "$files" ]; then
asciidoc-lint $files
fi
CI/CD Integration
name: Lint AsciiDoc
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint Documentation
run: |
pip install asciidoc-linter
asciidoc-lint docs/
Best Practices
Document Organization
-
Use consistent heading levels
-
Add blank lines around blocks
-
Use proper formatting
-
Include alt text for images
Error Resolution
Issue | Solution |
---|---|
Skipped heading level |
Ensure heading levels increment by one |
Missing space after = |
Add space after heading markers |
Multiple top-level headings |
Use only one level-1 heading per document |
Troubleshooting
Common Problems
-
Configuration file not found
-
Rule conflicts
-
Performance issues
-
False positives
Debug Mode
# Enable debug output
asciidoc-lint --debug document.adoc
# Show rule processing details
asciidoc-lint --verbose document.adoc
AsciiDoc Linter Rules
Heading Rules
HEAD001: Heading Hierarchy
Ensures proper heading level incrementation (no skipped levels).
Description
This rule checks that heading levels are not skipped. For example, you cannot go from a level 1 heading (=) directly to a level 3 heading (===) without having a level 2 heading (==) in between.
Examples
== Document Title (Level 1)
=== Section (Level 2)
==== Subsection (Level 3)
=== Another Section (Level 2)
== Document Title (Level 1)
==== Subsection (Level 3) // Error: Skipped Level 2
HEAD002: Heading Format
Ensures proper heading format (spacing and capitalization).
Description
This rule checks two aspects of heading format: 1. There must be a space after the = characters 2. The heading text should start with an uppercase letter
Examples
== Document Title
=== Section Title
==== Subsection Title
=Document Title // Error: No space after =
=== section title // Warning: Starts with lowercase
HEAD003: Multiple Top Level Headings
Ensures document has only one top-level heading.
Description
This rule checks that there is only one level 1 heading (=) in the document. Multiple top-level headings can indicate structural problems or accidentally split documents.
Examples
== Main Document Title
=== Section One
=== Section Two
== First Title
=== Section One
== Second Title // Error: Multiple top-level headings
Block Rules
BLOCK001: Unterminated Blocks
Checks for blocks that are not properly terminated.
Description
This rule ensures that all block delimiters are properly paired. Each opening delimiter must have a matching closing delimiter.
Supported Block Types
-
Listing blocks (----)
-
Example blocks (====)
-
Sidebar blocks (****)
-
Literal blocks (….)
-
Quote blocks (_\_)
-
Table blocks (|===)
-
Comment blocks (////)
-
Passthrough blocks (+\+).
Examples
[source,python]
def hello(): print("Hello, World!")
.Example Block ==== Some example content ====
[source,python]
def hello(): print("Hello, World!")
Some example content
==== BLOCK002: Block Spacing Verifies proper spacing around blocks. ===== Description This rule checks that blocks are properly separated from surrounding content with blank lines, improving readability. ===== Examples .Valid Block Spacing [source,asciidoc]
Some text before the block.
Block content
Some text after the block.
.Invalid Block Spacing [source,asciidoc]
Some text before the block. ---- // Warning: No blank line before block Block content
Some text after the block. // Warning: No blank line after block
=== Whitespace Rules
==== WS001: Whitespace Usage
Ensures proper whitespace usage throughout the document.
===== Checks Performed
-
Consecutive Empty Lines: No more than one consecutive empty line
-
List Marker Spacing: Proper space after list markers (*, -, .)
-
Admonition Block Spacing: Blank line before admonition blocks
-
Trailing Whitespace: No trailing spaces at end of lines
-
Tab Usage: No tabs (use spaces instead)
-
Section Title Spacing: Blank lines around section titles
===== Examples
== Document Title
=== Section Title
* List item 1
* List item 2
NOTE: This is a note.
Some text here.
== Document Title
=== Section Title // Missing blank line before
*Invalid list item // Missing space after marker
NOTE: Invalid note // Missing blank line before
Some text here // Trailing spaces
Tabbed line // Tab instead of spaces
Extra blank line // Too many blank lines
=== Image Rules
==== IMG001: Image Attributes
Verifies image attributes and file references.
===== Description
This rule checks: 1. All images have alt text 2. Referenced local images exist 3. Block images have titles 4. Image attributes are properly formatted
===== Examples
// Inline image with alt text
image:icon.png[Icon]
// Block image with title and alt text
.Figure 1: System Architecture
image::diagram.png[Architecture Diagram]
// External image with alt text
image:https://example.com/img.png[Example Image]
// Missing alt text
image:icon.png[]
// Missing title for block image
image::diagram.png[Diagram]
// Non-existent local file
image::missing.png[Missing Image]
=== Planned Rules
==== TABLE001: Table Formatting (Planned)
Will check table formatting consistency: * Column alignment * Header row presence * Cell content formatting
==== LINK001: Link Verification (Planned)
Will verify: * Internal cross-references * External link validity * Anchor definitions
==== FMT001: Format Consistency (Planned)
Will check: * Consistent emphasis style * List formatting * Admonition usage
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.