llms.txt

# DprintMarkdownFormatter Usage Guide for LLMs

This guide helps LLMs understand how to use the DprintMarkdownFormatter package effectively.

## Package Overview

DprintMarkdownFormatter is a fast, configurable markdown formatter for Elixir that uses Rust's `dprint-plugin-markdown` via NIFs. It provides:

- High-performance markdown formatting
- Mix format integration for .md files
- Module attribute formatting (@moduledoc, @doc, etc.)
- Sigil support (~M)
- Extensive configuration options

## Quick Start

### Installation

Add to `mix.exs`:

```elixir
def deps do
  [
    {:dprint_markdown_formatter, "~> 0.5.1"}
  ]
end
```


## Configuration

### Global Configuration (mix.exs)

```elixir
def project do
  [
    # ... other config
    dprint_markdown_formatter: [
      line_width: 100,
      text_wrap: :never,
      emphasis_kind: :underscores,
      format_module_attributes: true
    ]
  ]
end
```


### Configuration Options

| Option | Default | Values | Description |
|--------|---------|--------|-------------|
| `:line_width` | `80` | positive integer | Maximum line width |
| `:text_wrap` | `:always` | `:always`, `:never`, `:maintain` | Text wrapping behavior |
| `:emphasis_kind` | `:asterisks` | `:asterisks`, `:underscores` | Emphasis style (*text* vs _text_) |
| `:strong_kind` | `:asterisks` | `:asterisks`, `:underscores` | Strong text style (**text** vs __text__) |
| `:unordered_list_kind` | `:dashes` | `:dashes`, `:asterisks` | List style (- vs *) |
| `:new_line_kind` | `:auto` | `:auto`, `:lf`, `:crlf` | Line ending type |
| `:format_module_attributes` | `nil` | `nil`, `true`, `false`, `[atom()]` | Module attribute formatting |

**Note**: Configuration values can be atoms (`:never`) or strings (`"never"`). Atoms are preferred.

## Usage Patterns

### 1. Mix Format Integration

Add to `.formatter.exs`:

```elixir
[
  plugins: [DprintMarkdownFormatter],
  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "*.{md,markdown}"]
]
```

This enables automatic formatting of `.md` and `.markdown` files when running `mix format`.

### 2. Sigil Usage

```elixir
import DprintMarkdownFormatter.Sigil

# Sigil content gets formatted by mix format
markdown = ~M"""
# API   Documentation   

This is **bold** text with    extra   spaces.

*   Poorly   formatted   list
"""
```

### 3. Module Attribute Formatting

Enable in `mix.exs`:

```elixir
dprint_markdown_formatter: [
  format_module_attributes: true  # Format default attributes
]
```

This formats markdown in `@moduledoc`, `@doc`, `@typedoc`, `@shortdoc`, and `@deprecated`.

Custom attributes:

```elixir
dprint_markdown_formatter: [
  format_module_attributes: [:moduledoc, :doc, :custom_doc]
]
```

## Module Attribute Formatting

### Setup Requirements

1. Add plugin to `.formatter.exs`:
   ```elixir
   [
     plugins: [DprintMarkdownFormatter],
     inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "*.{md,markdown}"]
   ]
   ```

2. Configure in `mix.exs`:
   ```elixir
   dprint_markdown_formatter: [
     format_module_attributes: true
   ]
   ```

3. Run `mix format` on your `.ex` files

### How It Works

Before formatting:
```elixir
defmodule MyModule do
  @moduledoc """
  This is   messy   markdown.
  
  *   Poorly formatted list
  *   Another    item
  """
  
  @doc """
  Function docs with    extra   spaces.
  """
  def my_function, do: :ok
end
```

After `mix format`:
```elixir
defmodule MyModule do
  @moduledoc """
  This is messy markdown.
  
  - Properly formatted list
  - Another item
  """
  
  @doc """
  Function docs with extra spaces.
  """
  def my_function, do: :ok
end
```

## Common Use Cases

### 1. CI/CD Integration

```bash
# Check if markdown files are properly formatted
mix format --check-formatted
```

### 2. Custom Formatting Workflows

Use `mix format` with specific file patterns:

```bash
# Format all markdown files
mix format "**/*.md"

# Format specific directories
mix format "docs/**/*.{md,markdown}"
```

## Troubleshooting

### Module Attributes Not Being Formatted

**Check these steps in order:**

1. **Plugin configuration**: Ensure `.formatter.exs` has:
   ```elixir
   [
     plugins: [DprintMarkdownFormatter],
     inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "*.{md,markdown}"]
   ]
   ```

2. **Module attribute configuration**: Ensure `mix.exs` has:
   ```elixir
   dprint_markdown_formatter: [
     format_module_attributes: true  # or custom list
   ]
   ```

3. **Run on .ex files**: Module attributes are formatted when you run `mix format` on `.ex` files, not `.md` files.

4. **Check attribute names**: Default formatting only applies to `:moduledoc`, `:doc`, `:typedoc`, `:shortdoc`, `:deprecated`.

### Compilation Issues

If you see Rust compilation errors, force precompiled binaries:

```bash
export RUSTLER_PRECOMPILED_FORCE_BUILD=false
mix deps.get
mix compile
```

### Configuration Not Taking Effect

- **Global config**: Must be in `mix.exs` under `dprint_markdown_formatter` key
- **Per-call config**: Passed as second argument to `format/2`
- **Mix format**: Uses global config from `mix.exs`

### Performance Considerations

- **Large files**: The formatter is optimized for performance but very large files (>1MB) may take time
- **Batch processing**: For many files, consider parallel processing with `Task.async_stream/2`
- **Caching**: The formatter is deterministic - same input always produces same output

## Advanced Usage

### Custom Attribute Formatting

```elixir
# Format specific attributes only
dprint_markdown_formatter: [
  format_module_attributes: [
    :moduledoc,
    :doc,
    :api_doc,      # custom attribute
    :example_doc   # custom attribute
  ]
]
```

### Complex Configuration

```elixir
dprint_markdown_formatter: [
  line_width: 120,
  text_wrap: :never,
  emphasis_kind: :underscores,
  strong_kind: :underscores,
  unordered_list_kind: :asterisks,
  format_module_attributes: [
    :moduledoc,
    :doc,
    :typedoc,
    :spec_doc,
    :example_doc
  ]
]
```

### Integration with Other Tools

Use `mix format` in your build tools and CI:

```bash
# In your build scripts
mix format --check-formatted

# Format before generating docs
mix format && mix docs
```

## Best Practices

1. **Use atoms for config values**: `:never` instead of `"never"`
2. **Set reasonable line widths**: 80-120 characters work well
3. **Consider text wrapping**: `:maintain` preserves existing line breaks
4. **Test configuration**: Start with defaults, then customize
5. **Use in CI**: Add `mix format --check-formatted` to ensure consistent formatting
6. **Document custom attributes**: If using custom module attributes, document them for your team

## Performance Tips

- The formatter is very fast due to Rust implementation
- For large batches of files, use `Task.async_stream/2` for parallel processing
- Configuration parsing is cached, so repeated calls with same options are efficient
- Empty or very small strings are handled efficiently with early returns