Skip to content

Conversation

@neilberkman
Copy link

@neilberkman neilberkman commented Sep 6, 2025

Description

Fixes Surface formatter compatibility with Elixir 1.19.0-rc.0 while maintaining backward compatibility with Elixir 1.18.

Problem

When running mix format on .sface files with Elixir 1.19.0-rc.0, the Surface formatter crashes with:

** (Protocol.UndefinedError) protocol Enumerable not implemented for BitString

Got value:
    "[for: for_id]"

    (surface 0.12.1) lib/surface/formatter/phases/render.ex:451

Root Cause

In lib/surface/formatter/phases/render.ex, the formatter tries to use Enum.slice on the result of Code.format_string!, which returns iodata (not a list). Elixir 1.19 is stricter about this type mismatch.

Solution

Changed from:

|> Code.format_string!(locals_without_parens: [...: 1])
|> Enum.slice(1..-2//1)
|> to_string()

To:

|> Code.format_string!(locals_without_parens: [...: 1])
|> IO.iodata_to_binary()
|> String.slice(1..-2//1)

Also removed the redundant to_string() call in the else branch since IO.iodata_to_binary() already returns a binary.

Compatibility

This fix is fully backward compatible with Elixir 1.18:

  • IO.iodata_to_binary/1 exists in both Elixir 1.18 and 1.19
  • String.slice/2 exists in both versions
  • Both return the same result as the original code

Testing

Tested formatting Surface templates with:

  • Elixir 1.18.x
  • Elixir 1.19.0-rc.0

Fixes #766

Code.format_string! returns iodata, not a list, so Enum.slice fails.
Changed to use IO.iodata_to_binary and String.slice instead.

This fixes the Enumerable protocol error when formatting Surface templates
with keyword list attributes in Elixir 1.19.0-rc.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Surface.Formatter.Plugin incompatible with Elixir 1.19.0-rc.0 - Enumerable protocol error for keyword lists

1 participant