# 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 Strategy and Guide
..1. Test Strategy
Goals and Objectives
The testing strategy for the AsciiDoc Linter aims to:
-
Ensure reliable detection of AsciiDoc formatting issues
-
Prevent false positives that could frustrate users
-
Maintain high code quality through comprehensive testing
-
Enable safe refactoring through good test coverage
-
Support rapid development through automated testing
Test Levels
Unit Tests
-
Test individual rules in isolation
-
Verify rule logic and error detection
-
Cover edge cases and special scenarios
-
Test configuration options
Integration Tests
-
Test interaction between parser and rules
-
Verify correct document processing
-
Test CLI interface and options
-
Test reporter output formats
System Tests
-
End-to-end testing of the linter
-
Test with real AsciiDoc documents
-
Verify correct error reporting
-
Test performance with large documents
Test Coverage Goals
Component | Target Coverage | Current Coverage |
---|---|---|
Core (parser, linter) |
90% |
0% |
Rules |
95% |
88% |
CLI |
80% |
0% |
Reporter |
85% |
0% |
Overall |
90% |
61% |
Quality Metrics
-
Line Coverage: Minimum 90%
-
Branch Coverage: Minimum 85%
-
Mutation Score: Minimum 80%
-
Test Success Rate: 100%
-
No known bugs in production
..2. Test Implementation
Test Organization
tests/
├── __init__.py
├── rules/ # Rule-specific tests
│ ├── __init__.py
│ ├── test_heading_rules.py
│ ├── test_block_rules.py
│ ├── test_image_rules.py
│ └── test_whitespace_rules.py
├── integration/ # Integration tests
│ ├── __init__.py
│ ├── test_parser_rules.py
│ └── test_cli_reporter.py
├── system/ # System tests
│ ├── __init__.py
│ ├── test_large_docs.py
│ └── test_real_projects.py
├── test_cli.py # CLI tests
├── test_parser.py # Parser tests
├── test_reporter.py # Reporter tests
└── test_linter.py # Core linter tests
Test Patterns
Rule Tests
def test_rule_pattern(self):
# Given: Setup test data and context
content = "test content"
rule = TestRule(config)
# When: Execute the rule
findings = rule.check(content)
# Then: Verify results
assert_findings(findings)
Integration Tests
def test_integration_pattern(self):
# Given: Setup test environment
doc = create_test_document()
linter = setup_linter()
# When: Process document
results = linter.process(doc)
# Then: Verify complete workflow
verify_results(results)
Test Data Management
Test Documents
-
Maintain a collection of test documents
-
Include both valid and invalid examples
-
Document the purpose of each test file
-
Version control test data
Test Fixtures
-
Use pytest fixtures for common setup
-
Share test data between related tests
-
Clean up test environment after each test
-
Mock external dependencies
..3. Running Tests
Local Development
# Run all tests
python run_tests.py
# Run with coverage
coverage run -m pytest
coverage report
coverage html
# Run specific test categories
pytest tests/rules/
pytest tests/integration/
pytest tests/system/
Continuous Integration
GitHub Actions Workflow
name: Test Suite
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, "3.10"]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
coverage run -m pytest
coverage report
coverage xml
- name: Upload coverage
uses: codecov/codecov-action@v2
..4. Test Maintenance
Regular Activities
-
Review test coverage reports weekly
-
Update tests for new features
-
Refactor tests when needed
-
Review test performance
-
Update test documentation
Quality Checks
-
Run mutation testing monthly
-
Review test maintainability
-
Check for flaky tests
-
Verify test isolation
..5. Appendix
Test Templates
Unit Test Template
class TestRuleName(unittest.TestCase):
def setUp(self):
"""Setup test environment"""
self.rule = RuleUnderTest()
def test_valid_case(self):
"""Test with valid input"""
# Given
content = "valid content"
# When
findings = self.rule.check(content)
# Then
self.assertEqual(len(findings), 0)
def test_invalid_case(self):
"""Test with invalid input"""
# Given
content = "invalid content"
# When
findings = self.rule.check(content)
# Then
self.assertEqual(len(findings), 1)
self.assertEqual(findings[0].severity, Severity.ERROR)
Test Checklists
New Feature Checklist
-
Unit tests written
-
Integration tests updated
-
System tests verified
-
Coverage goals met
-
Documentation updated
Test Review Checklist
-
Tests follow patterns
-
Coverage adequate
-
Edge cases covered
-
Error cases tested
-
Documentation clear
..6. References
.1. Usage Guide
.1.1. 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 |
.1.2. 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
.1.3. 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
.1.4. 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/
.1.5. 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 |
.1.6. 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
.2. AsciiDoc Linter Rules
.2.1. 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
.2.2. 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.