Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/actions/setup-elixir-rust/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ runs:
version-type: ${{ inputs.elixir-version == '' && 'strict' || '' }}
elixir-version: ${{ inputs.elixir-version }}
otp-version: ${{ inputs.otp-version }}
# builds.hex.pm CDN cert trips OTP TLS validation (key_usage_mismatch); fall back to jsdelivr
hexpm-mirrors: |
https://builds.hex.pm
https://cdn.jsdelivr.net/hex

- name: Cache Elixir Dependencies
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
_build
Expand All @@ -60,7 +64,7 @@ runs:

- name: Cache Rust Dependencies
if: inputs.skip-rust != 'true'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
otp-version: ${{ steps.setup-env.outputs.otp-version }}
elixir-version: ${{ steps.setup-env.outputs.elixir-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- id: setup-env
uses: ./.github/actions/setup-elixir-rust
Expand All @@ -42,7 +42,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: ./.github/actions/setup-elixir-rust

Expand All @@ -57,15 +57,15 @@ jobs:
matrix:
check: [credo, dialyzer]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: ./.github/actions/setup-elixir-rust
with:
cache-key-suffix: -${{ matrix.check }}

- name: Cache Dialyzer PLTs
if: matrix.check == 'dialyzer'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: priv/plts
key: ${{ runner.os }}-plt-${{ needs.build.outputs.otp-version }}-${{ needs.build.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }}
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:
- elixir: "1.16.3"
otp: "26.2"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: ./.github/actions/setup-elixir-rust
with:
Expand Down
28 changes: 14 additions & 14 deletions .github/workflows/precompiled-nifs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,37 @@ jobs:
matrix:
nif: ["2.16", "2.17"]
job:
- { target: x86_64-apple-darwin, os: macos-13 }
# Use native Apple Silicon runner for aarch64-apple-darwin to avoid cross-compilation issues
# macos-13 (Intel) runners are being retired and starve the queue; build x86_64 via cross-compile on Apple Silicon
- { target: x86_64-apple-darwin, os: macos-14 }
- { target: aarch64-apple-darwin, os: macos-14 }
- {
target: aarch64-unknown-linux-gnu,
os: ubuntu-22.04,
os: ubuntu-24.04,
use-cross: true,
}
- {
target: aarch64-unknown-linux-musl,
os: ubuntu-22.04,
os: ubuntu-24.04,
use-cross: true,
}
- { target: x86_64-pc-windows-gnu, os: windows-2022 }
- { target: x86_64-pc-windows-msvc, os: windows-2022 }
- { target: aarch64-pc-windows-msvc, os: windows-2022 }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
- { target: x86_64-pc-windows-gnu, os: windows-2025 }
- { target: x86_64-pc-windows-msvc, os: windows-2025 }
- { target: aarch64-pc-windows-msvc, os: windows-2025 }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-24.04 }
- {
target: x86_64-unknown-linux-musl,
os: ubuntu-22.04,
os: ubuntu-24.04,
use-cross: true,
}
- {
target: riscv64gc-unknown-linux-gnu,
os: ubuntu-22.04,
os: ubuntu-24.04,
use-cross: true,
}

steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Extract project version
shell: bash
Expand All @@ -76,7 +76,7 @@ jobs:

- name: Build the project
id: build-crate
uses: philss/rustler-precompiled-action@v1.1.4
uses: philss/rustler-precompiled-action@v1.1.5
with:
project-name: dprint_markdown_formatter
project-version: ${{ env.PROJECT_VERSION }}
Expand All @@ -87,13 +87,13 @@ jobs:
project-dir: native/dprint_markdown_formatter

- name: Artifact upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.build-crate.outputs.file-name }}
path: ${{ steps.build-crate.outputs.file-path }}

- name: Publish archives and packages
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
files: |
${{ steps.build-crate.outputs.file-path }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup Elixir and Rust
uses: ./.github/actions/setup-elixir-rust
Expand Down
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ end
- `:text_wrap` - "always", "never", "maintain" (default: "always")
- `:emphasis_kind` - "asterisks", "underscores" (default: "asterisks")
- `:strong_kind` - "asterisks", "underscores" (default: "asterisks")
- `:new_line_kind` - "auto", "lf", "crlf" (default: "auto")
- `:unordered_list_kind` - "dashes", "asterisks" (default: "dashes")
- `:heading_kind` - "atx", "setext" (default: "atx")
- `:list_indent_kind` - "common_mark", "python_markdown" (default:
"common_mark")
- `:format_module_attributes` - Configure attribute formatting:
- `nil` (default) - Skip all formatting
- `true` - Format `:moduledoc`, `:doc`, `:typedoc`, `:shortdoc`, `:deprecated`
Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,17 @@ end

### Available Options

| Option | Default | Description |
| --------------------------- | ------------ | ----------------------------------------------- |
| `:line_width` | `80` | Maximum line width |
| `:text_wrap` | `:always` | Text wrapping: `:always`, `:never`, `:maintain` |
| `:emphasis_kind` | `:asterisks` | Emphasis style: `:asterisks`, `:underscores` |
| `:strong_kind` | `:asterisks` | Strong text style: `:asterisks`, `:underscores` |
| `:unordered_list_kind` | `:dashes` | List style: `:dashes`, `:asterisks` |
| `:format_module_attributes` | `nil` | Module attribute formatting (see below) |
| Option | Default | Description |
| --------------------------- | -------------- | ------------------------------------------------------ |
| `:line_width` | `80` | Maximum line width |
| `:text_wrap` | `:always` | Text wrapping: `:always`, `:never`, `:maintain` |
| `:emphasis_kind` | `:asterisks` | Emphasis style: `:asterisks`, `:underscores` |
| `:strong_kind` | `:asterisks` | Strong text style: `:asterisks`, `:underscores` |
| `:new_line_kind` | `:auto` | Line ending type: `:auto`, `:lf`, `:crlf` |
| `:unordered_list_kind` | `:dashes` | List style: `:dashes`, `:asterisks` |
| `:heading_kind` | `:atx` | Heading style: `:atx` (`#`), `:setext` (`===`) |
| `:list_indent_kind` | `:common_mark` | Nested list indent: `:common_mark`, `:python_markdown` |
| `:format_module_attributes` | `nil` | Module attribute formatting (see below) |

**Note:** Configuration values can be provided as atoms (`:never`) or strings
(`"never"`). Atoms are preferred for consistency with Elixir conventions.
Expand Down
3 changes: 3 additions & 0 deletions lib/dprint_markdown_formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ defmodule DprintMarkdownFormatter do
(default: `:dashes`)
- `:heading_kind` - Heading style for level 1/2 headings: `:atx` (`#`/`##`),
`:setext` (`===`/`---`) (default: `:atx`)
- `:list_indent_kind` - Nested list indent style: `:common_mark` (indent to
marker width), `:python_markdown` (indent to at least 4 spaces) (default:
`:common_mark`)

**Note:** Configuration values can be provided as atoms (`:never`) or strings
(`"never"`) via `mix.exs`. Runtime options passed to `format/2` must be atoms.
Expand Down
15 changes: 11 additions & 4 deletions lib/dprint_markdown_formatter/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule DprintMarkdownFormatter.Config do
@typep new_line_kind_option() :: :auto | :lf | :crlf
@typep unordered_list_kind_option() :: :dashes | :asterisks
@typep heading_kind_option() :: :atx | :setext
@typep list_indent_kind_option() :: :common_mark | :python_markdown
@typep module_attributes_option() :: nil | boolean() | [atom()]

@typep nif_config_map() :: %{
Expand All @@ -20,7 +21,8 @@ defmodule DprintMarkdownFormatter.Config do
strong_kind: atom(),
new_line_kind: atom(),
unordered_list_kind: atom(),
heading_kind: atom()
heading_kind: atom(),
list_indent_kind: atom()
}

@atom_choices %{
Expand All @@ -29,7 +31,8 @@ defmodule DprintMarkdownFormatter.Config do
strong_kind: [:asterisks, :underscores],
new_line_kind: [:auto, :lf, :crlf],
unordered_list_kind: [:dashes, :asterisks],
heading_kind: [:atx, :setext]
heading_kind: [:atx, :setext],
list_indent_kind: [:common_mark, :python_markdown]
}

typed_structor enforce: true do
Expand All @@ -40,6 +43,7 @@ defmodule DprintMarkdownFormatter.Config do
field :new_line_kind, new_line_kind_option(), default: :auto
field :unordered_list_kind, unordered_list_kind_option(), default: :dashes
field :heading_kind, heading_kind_option(), default: :atx
field :list_indent_kind, list_indent_kind_option(), default: :common_mark
field :format_module_attributes, module_attributes_option(), default: nil
end

Expand All @@ -57,6 +61,7 @@ defmodule DprintMarkdownFormatter.Config do
new_line_kind: :auto,
unordered_list_kind: :dashes,
heading_kind: :atx,
list_indent_kind: :common_mark,
format_module_attributes: nil
}
"""
Expand Down Expand Up @@ -188,7 +193,8 @@ defmodule DprintMarkdownFormatter.Config do
strong_kind: :asterisks,
new_line_kind: :auto,
unordered_list_kind: :dashes,
heading_kind: :atx
heading_kind: :atx,
list_indent_kind: :common_mark
}
"""
@spec to_nif_config(t()) :: nif_config_map()
Expand All @@ -200,7 +206,8 @@ defmodule DprintMarkdownFormatter.Config do
strong_kind: config.strong_kind,
new_line_kind: config.new_line_kind,
unordered_list_kind: config.unordered_list_kind,
heading_kind: config.heading_kind
heading_kind: config.heading_kind,
list_indent_kind: config.list_indent_kind
}
end

Expand Down
6 changes: 4 additions & 2 deletions lib/dprint_markdown_formatter/native.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ defmodule DprintMarkdownFormatter.Native do
@typep new_line_kind_option() :: :auto | :lf | :crlf
@typep unordered_list_kind_option() :: :dashes | :asterisks
@typep heading_kind_option() :: :atx | :setext
@typep list_indent_kind_option() :: :common_mark | :python_markdown

@typep format_options() :: %{
line_width: pos_integer(),
Expand All @@ -34,7 +35,8 @@ defmodule DprintMarkdownFormatter.Native do
strong_kind: strong_kind_option(),
new_line_kind: new_line_kind_option(),
unordered_list_kind: unordered_list_kind_option(),
heading_kind: heading_kind_option()
heading_kind: heading_kind_option(),
list_indent_kind: list_indent_kind_option()
}

@doc """
Expand All @@ -55,7 +57,7 @@ defmodule DprintMarkdownFormatter.Native do

## Examples

iex> options = %{line_width: 80, text_wrap: :always, emphasis_kind: :asterisks, strong_kind: :asterisks, new_line_kind: :auto, unordered_list_kind: :dashes, heading_kind: :atx}
iex> options = %{line_width: 80, text_wrap: :always, emphasis_kind: :asterisks, strong_kind: :asterisks, new_line_kind: :auto, unordered_list_kind: :dashes, heading_kind: :atx, list_indent_kind: :common_mark}
iex> DprintMarkdownFormatter.Native.format_markdown("# Hello World", options)
{:ok, "# Hello World\\n"}

Expand Down
8 changes: 8 additions & 0 deletions lib/dprint_markdown_formatter/validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ defmodule DprintMarkdownFormatter.Validator do
{:ok, _new_line_kind} <- validate_new_line_kind(config.new_line_kind),
{:ok, _unordered_list_kind} <- validate_unordered_list_kind(config.unordered_list_kind),
{:ok, _heading_kind} <- validate_heading_kind(config.heading_kind),
{:ok, _list_indent_kind} <- validate_list_indent_kind(config.list_indent_kind),
{:ok, _format_module_attributes} <-
validate_format_module_attributes(config.format_module_attributes) do
{:ok, config}
Expand Down Expand Up @@ -164,6 +165,10 @@ defmodule DprintMarkdownFormatter.Validator do
defp validate_option_value(:new_line_kind, value), do: validate_new_line_kind(value)
defp validate_option_value(:unordered_list_kind, value), do: validate_unordered_list_kind(value)
defp validate_option_value(:heading_kind, value), do: validate_heading_kind(value)

defp validate_option_value(:list_indent_kind, value),
do: validate_list_indent_kind(value)

defp validate_option_value(:extension, value), do: validate_extension(value)
defp validate_option_value(:sigil, value), do: validate_sigil(value)

Expand Down Expand Up @@ -212,6 +217,9 @@ defmodule DprintMarkdownFormatter.Validator do
defp validate_heading_kind(value),
do: validate_atom_choice(value, :heading_kind, [:atx, :setext])

defp validate_list_indent_kind(value),
do: validate_atom_choice(value, :list_indent_kind, [:common_mark, :python_markdown])

defp validate_atom_choice(value, field, valid_choices) when is_atom(value) do
if value in valid_choices do
{:ok, value}
Expand Down
Loading
Loading