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

Table 1. Technology Stack
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.

# 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 .
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
  1. 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
  2. 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")
  3. Register the rule in the linter

  4. 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

  1. Create feature branch

  2. Implement changes

  3. Add/update tests

  4. Update documentation

  5. Run full test suite

  6. Submit PR

Release Process

  1. Update version number in init.py

  2. Update changelog

  3. Run full test suite

  4. Create release notes

  5. Tag release

  6. Build and publish

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
  1. Arrange: Set up test data

  2. Act: Execute the code

  3. 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
GitHub Actions Example
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
Table 2. Common Issues and Solutions
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
Valid Heading Hierarchy
== Document Title (Level 1)

=== Section (Level 2)

==== Subsection (Level 3)

=== Another Section (Level 2)
Invalid Heading Hierarchy
== 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
Valid Heading Format
== Document Title
=== Section Title
==== Subsection Title
Invalid Heading Format
=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
Valid Single Top Level
== Main Document Title

=== Section One
=== Section Two
Invalid Multiple Top Level
== 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
Valid Block Termination
[source,python]

def hello(): print("Hello, World!")

.Example Block
====
Some example content
====
Invalid Block Termination
[source,python]

def hello(): print("Hello, World!")

Example 1. Example Block

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

  1. Consecutive Empty Lines: No more than one consecutive empty line

  2. List Marker Spacing: Proper space after list markers (*, -, .)

  3. Admonition Block Spacing: Blank line before admonition blocks

  4. Trailing Whitespace: No trailing spaces at end of lines

  5. Tab Usage: No tabs (use spaces instead)

  6. Section Title Spacing: Blank lines around section titles

===== Examples

Valid Whitespace Usage
== Document Title

=== Section Title

* List item 1
* List item 2

NOTE: This is a note.

Some text here.
Invalid Whitespace Usage
== 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

Valid Image Usage
// 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]
Invalid Image Usage
// 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