From 7dc0a7f50ef4a9446be328c0399c14c7deaa9f63 Mon Sep 17 00:00:00 2001 From: Phil Chen <06fahchen@gmail.com> Date: Sun, 28 Jun 2026 12:50:01 +0900 Subject: [PATCH 1/2] feat: upgrade CI/deps and add list_indent_kind option Fix Precomp NIFs CI: x86_64-apple-darwin moved off retired macos-13 (Intel) runner to macos-14 cross-compile; bump linux/windows runners. Upgrade GH Actions to Node 24 versions (checkout v5, upload-artifact v5, action-gh-release v3, cache v5, rustler-precompiled-action v1.1.5). Upgrade deps: rustler 0.36->0.38 (rust-toolchain 1.88->1.92), dprint-plugin-markdown 0.21.1->0.22.1, rustler_precompiled 0.8->0.9, typed_structor 0.5->0.6, mimic 1->2, ex_doc/credo/dialyxir/sourceror. Add full list_indent_kind support (:common_mark default = prior behavior, :python_markdown) across Rust NIF, Config, Validator, Native, docs, tests (0.22 made the field required). Drop hand-written llms.txt; ex_doc 0.40 auto-generates it. Co-Authored-By: Claude Opus 4.7 --- .github/actions/setup-elixir-rust/action.yml | 4 +- .github/workflows/ci.yml | 10 +- .github/workflows/precompiled-nifs.yml | 28 +- .github/workflows/publish.yml | 2 +- CLAUDE.md | 4 + README.md | 19 +- lib/dprint_markdown_formatter.ex | 3 + lib/dprint_markdown_formatter/config.ex | 15 +- lib/dprint_markdown_formatter/native.ex | 6 +- lib/dprint_markdown_formatter/validator.ex | 8 + llms.txt | 299 ------------------ mix.exs | 11 +- mix.lock | 28 +- native/dprint_markdown_formatter/Cargo.toml | 4 +- .../rust-toolchain.toml | 2 +- native/dprint_markdown_formatter/src/lib.rs | 13 +- test/dprint_markdown_formatter_test.exs | 19 +- 17 files changed, 115 insertions(+), 360 deletions(-) delete mode 100644 llms.txt diff --git a/.github/actions/setup-elixir-rust/action.yml b/.github/actions/setup-elixir-rust/action.yml index e980778..f59647f 100644 --- a/.github/actions/setup-elixir-rust/action.yml +++ b/.github/actions/setup-elixir-rust/action.yml @@ -40,7 +40,7 @@ runs: otp-version: ${{ inputs.otp-version }} - name: Cache Elixir Dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | _build @@ -60,7 +60,7 @@ runs: - name: Cache Rust Dependencies if: inputs.skip-rust != 'true' - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ~/.cargo/registry diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e34bc48..20ce86f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -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 @@ -57,7 +57,7 @@ jobs: matrix: check: [credo, dialyzer] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/setup-elixir-rust with: @@ -65,7 +65,7 @@ jobs: - 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') }} @@ -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: diff --git a/.github/workflows/precompiled-nifs.yml b/.github/workflows/precompiled-nifs.yml index 242d7e3..a74bed8 100644 --- a/.github/workflows/precompiled-nifs.yml +++ b/.github/workflows/precompiled-nifs.yml @@ -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 @@ -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 }} @@ -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 }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7c94e79..b918d56 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md index f1283e8..7d13705 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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` diff --git a/README.md b/README.md index 994970f..5750573 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lib/dprint_markdown_formatter.ex b/lib/dprint_markdown_formatter.ex index 237ba46..ac4d12d 100644 --- a/lib/dprint_markdown_formatter.ex +++ b/lib/dprint_markdown_formatter.ex @@ -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. diff --git a/lib/dprint_markdown_formatter/config.ex b/lib/dprint_markdown_formatter/config.ex index 5adb1b0..6630886 100644 --- a/lib/dprint_markdown_formatter/config.ex +++ b/lib/dprint_markdown_formatter/config.ex @@ -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() :: %{ @@ -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 %{ @@ -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 @@ -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 @@ -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 } """ @@ -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() @@ -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 diff --git a/lib/dprint_markdown_formatter/native.ex b/lib/dprint_markdown_formatter/native.ex index f76edc0..ab288d2 100644 --- a/lib/dprint_markdown_formatter/native.ex +++ b/lib/dprint_markdown_formatter/native.ex @@ -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(), @@ -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 """ @@ -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"} diff --git a/lib/dprint_markdown_formatter/validator.ex b/lib/dprint_markdown_formatter/validator.ex index db4aa3c..fcc61fc 100644 --- a/lib/dprint_markdown_formatter/validator.ex +++ b/lib/dprint_markdown_formatter/validator.ex @@ -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} @@ -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) @@ -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} diff --git a/llms.txt b/llms.txt deleted file mode 100644 index 6c2d38e..0000000 --- a/llms.txt +++ /dev/null @@ -1,299 +0,0 @@ -# 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 diff --git a/mix.exs b/mix.exs index ad150d0..7dec552 100644 --- a/mix.exs +++ b/mix.exs @@ -33,11 +33,11 @@ defmodule DprintMarkdownFormatter.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:rustler_precompiled, "~> 0.8"}, - {:rustler, "~> 0.36.0", optional: true}, + {:rustler_precompiled, "~> 0.9"}, + {:rustler, "~> 0.38", optional: true}, {:sourceror, "~> 1.0"}, - {:typed_structor, "~> 0.5"}, - {:mimic, "~> 1.7", only: :test}, + {:typed_structor, "~> 0.6"}, + {:mimic, "~> 2.0", only: :test}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, {:ex_doc, "~> 0.34", only: :dev, runtime: false} @@ -54,7 +54,6 @@ defmodule DprintMarkdownFormatter.MixProject do "mix.exs", "README.md", "LICENSE", - "llms.txt", "native/dprint_markdown_formatter/*.*", "native/dprint_markdown_formatter/.cargo/", "native/dprint_markdown_formatter/src/", @@ -85,7 +84,7 @@ defmodule DprintMarkdownFormatter.MixProject do main: "readme", source_url: @source_url, source_ref: "v#{@version}", - extras: ["README.md", "llms.txt"], + extras: ["README.md"], groups_for_modules: [ Core: [ DprintMarkdownFormatter, diff --git a/mix.lock b/mix.lock index a5de75f..756b23e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,22 +1,22 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, - "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, - "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, - "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "ham": {:hex, :ham, "0.3.0", "7cd031b4a55fba219c11553e7b13ba73bd86eab4034518445eff1e038cb9a44d", [:mix], [], "hexpm", "7d6c6b73d7a6a83233876cc1b06a4d9b5de05562b228effda4532f9a49852bf6"}, - "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, + "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.45", "cba8369ab2a1342e419bc2760eec731b17be828941dcf494045d44766227e1d5", [:mix], [], "hexpm", "d3ec045bf122965db20c0bdb420e19ee1415843135327124918473feb4b328e8"}, + "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, + "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, + "ham": {:hex, :ham, "0.3.2", "02ae195f49970ef667faf9d01bc454fb80909a83d6c775bcac724ca567aeb7b3", [:mix], [], "hexpm", "b71cc684c0e5a3d32b5f94b186770551509e93a9ae44ca1c1a313700f2f6a69a"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "mimic": {:hex, :mimic, "1.12.0", "34c9d1fb8e756df09ca5f96861d273f2bb01063df1a6a51a4c101f9ad7f07a9c", [:mix], [{:ham, "~> 0.2", [hex: :ham, repo: "hexpm", optional: false]}], "hexpm", "eaa43d495d6f3bc8099b28886e05a1b09a2a6be083f6385c3abc17599e5e2c43"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, + "mimic": {:hex, :mimic, "2.3.0", "88b1d13c285e57df6ea57204317bb56e49e7329668006cdcb80a9aafc73a9616", [:mix], [{:ham, "~> 0.3", [hex: :ham, repo: "hexpm", optional: false]}], "hexpm", "52771f23689398c5d41c7d05e91c2c28e10df273b784f40ca8b02e35e46850d3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "rustler": {:hex, :rustler, "0.36.2", "6c2142f912166dfd364017ab2bf61242d4a5a3c88e7b872744642ae004b82501", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.7", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "93832a6dbc1166739a19cd0c25e110e4cf891f16795deb9361dfcae95f6c88fe"}, - "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.2", "5f25cbe220a8fac3e7ad62e6f950fcdca5a5a5f8501835d2823e8c74bf4268d5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "63d1bd5f8e23096d1ff851839923162096364bac8656a4a3c00d1fff8e83ee0a"}, - "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, + "rustler": {:hex, :rustler, "0.38.0", "7a8906998ff0d28e3021c0a73264abcda719bda344b2e58307c6805b0f87c9b4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "704c03c1bf66be12b031c5a389347b91c81c5cb819a24b068b0de36fe4a5652a"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.9.0", "3a052eda09f3d2436364645cc1f13279cf95db310eb0c17b0d8f25484b233aa0", [:mix], [{:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "471d97315bd3bf7b64623418b3693eedd8e47de3d1cb79a0ac8f9da7d770d94c"}, + "sourceror": {:hex, :sourceror, "1.12.2", "85bfd48159f020c0cbfc72f289f11456fdc05dc43719b6f2589fb969faefa113", [:mix], [], "hexpm", "da37d3da09c5b890528802c7056a8f585a061973820d7656b6e3649c14f0e9cb"}, "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, - "typed_structor": {:hex, :typed_structor, "0.5.0", "a813fd8bafbff993c0839914c5c32ac0e5c5594ed96f7621813a8831a1991162", [:mix], [], "hexpm", "649cebfeac71c6bf2be2deaa2642af07fb88a1163fa026f84925470ea428004a"}, + "typed_structor": {:hex, :typed_structor, "0.6.1", "5a1e9bdb06192e493cbe205133f3a69ca73db23901d8ca58e804fdd52ce0c46b", [:mix], [], "hexpm", "4b896a1fe0eb23eaccb7dac10849379748816a8824b0fe98ca3ebb3923e3d8c7"}, } diff --git a/native/dprint_markdown_formatter/Cargo.toml b/native/dprint_markdown_formatter/Cargo.toml index d30d3fc..d7a95ef 100644 --- a/native/dprint_markdown_formatter/Cargo.toml +++ b/native/dprint_markdown_formatter/Cargo.toml @@ -8,8 +8,8 @@ name = "dprint_markdown_formatter" crate-type = ["cdylib"] [dependencies] -rustler = { version = "0.36.0" } -dprint-plugin-markdown = "0.21.1" +rustler = { version = "0.38.0" } +dprint-plugin-markdown = "0.22.1" dprint-core = "0.67.4" [features] diff --git a/native/dprint_markdown_formatter/rust-toolchain.toml b/native/dprint_markdown_formatter/rust-toolchain.toml index 70e1fb5..1db8f94 100644 --- a/native/dprint_markdown_formatter/rust-toolchain.toml +++ b/native/dprint_markdown_formatter/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.88.0" +channel = "1.92" components = ["rustfmt", "clippy"] diff --git a/native/dprint_markdown_formatter/src/lib.rs b/native/dprint_markdown_formatter/src/lib.rs index 8ccb3cd..9b7c2b8 100644 --- a/native/dprint_markdown_formatter/src/lib.rs +++ b/native/dprint_markdown_formatter/src/lib.rs @@ -1,6 +1,7 @@ use dprint_core::configuration::NewLineKind; use dprint_plugin_markdown::configuration::{ - Configuration, EmphasisKind, HeadingKind, StrongKind, TextWrap, UnorderedListKind, + Configuration, EmphasisKind, HeadingKind, ListIndentKind, StrongKind, TextWrap, + UnorderedListKind, }; use dprint_plugin_markdown::format_text; use rustler::{Atom, Term}; @@ -15,6 +16,7 @@ rustler::atoms! { new_line_kind, unordered_list_kind, heading_kind, + list_indent_kind, always, never, maintain, @@ -26,6 +28,8 @@ rustler::atoms! { dashes, atx, setext, + common_mark, + python_markdown, } /// Simple NIF function that receives a config map from Elixir @@ -62,6 +66,7 @@ fn build_dprint_config(map: HashMap) -> Result) -> Result HeadingKind::Setext, }); +build_enum_option!(build_list_indent_kind, list_indent_kind, ListIndentKind, { + common_mark => ListIndentKind::CommonMark, + python_markdown => ListIndentKind::PythonMarkdown, +}); + rustler::init!("Elixir.DprintMarkdownFormatter.Native"); diff --git a/test/dprint_markdown_formatter_test.exs b/test/dprint_markdown_formatter_test.exs index 13ab5af..8f5ec93 100644 --- a/test/dprint_markdown_formatter_test.exs +++ b/test/dprint_markdown_formatter_test.exs @@ -263,7 +263,8 @@ defmodule DprintMarkdownFormatterTest do strong_kind: :asterisks, new_line_kind: :auto, unordered_list_kind: :asterisks, - heading_kind: :atx + heading_kind: :atx, + list_indent_kind: :common_mark } result = DprintMarkdownFormatter.Native.format_markdown(input, nif_config) @@ -310,6 +311,22 @@ defmodule DprintMarkdownFormatterTest do end end + describe "format/2 with list_indent_kind" do + test "defaults to common_mark (marker-width indent)" do + assert DprintMarkdownFormatter.format("- a\n - b", []) == "- a\n - b\n" + end + + test "python_markdown indents nested lists to at least 4 spaces" do + assert DprintMarkdownFormatter.format("- a\n - b", list_indent_kind: :python_markdown) == + "- a\n - b\n" + end + + test "invalid list_indent_kind does not crash; format/2 returns original contents" do + assert DprintMarkdownFormatter.format("- a\n - b", list_indent_kind: :bogus) == + "- a\n - b" + end + end + describe "Mix.Tasks.Format behavior" do test "features/1 returns correct sigils and extensions" do features = DprintMarkdownFormatter.features([]) From 7daf5b4b1f1378070bacf09083bd5798bc9e07d4 Mon Sep 17 00:00:00 2001 From: Phil Chen <06fahchen@gmail.com> Date: Sun, 28 Jun 2026 12:56:24 +0900 Subject: [PATCH 2/2] fix(ci): add jsdelivr hex.pm mirror fallback builds.hex.pm CDN serves a cert chain that OTP's TLS validation rejects (key_usage_mismatch), breaking mix local.rebar/hex install. Add jsdelivr as a fallback mirror so setup-beam retries on TLS failure. Co-Authored-By: Claude Opus 4.7 --- .github/actions/setup-elixir-rust/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/setup-elixir-rust/action.yml b/.github/actions/setup-elixir-rust/action.yml index f59647f..c57fb95 100644 --- a/.github/actions/setup-elixir-rust/action.yml +++ b/.github/actions/setup-elixir-rust/action.yml @@ -38,6 +38,10 @@ 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@v5