Skip to content

Support ... in list patterns#8

Merged
dannote merged 1 commit into
elixir-vibe:masterfrom
rodrigues:vr/list_ellipsis
Jul 20, 2026
Merged

Support ... in list patterns#8
dannote merged 1 commit into
elixir-vibe:masterfrom
rodrigues:vr/list_ellipsis

Conversation

@rodrigues

Copy link
Copy Markdown
Contributor

... now works inside list patterns, so you can match lists by their edges and ignore the middle:

Pattern Matches
[...] any list, including empty
[1, ...] any list whose leading element is 1
[x, y, z, ...] any list of ≥3 elements, capturing the first three
[..., y, z] any list of ≥2 elements, capturing the last two
[first, ..., last] captures both ends; ... absorbs the middle

Fixed-length list patterns ([1, 2, 3], [_, _, _]) are unchanged — still an exact-length match.

Before

$ mix ex_ast.search "[first, ..., last]" router.ex

0 match(es)

After

$ mix ex_ast.search "[first, ..., last]" router.ex
router.ex:3
  [
    {:get, "/", Home},
    {:get, "/about", About},
    {:post, "/users", Users}
  ]
  first: {:get, "/", Home}
  last: {:post, "/users", Users}

1 match(es)
$ mix ex_ast.search "[{:count, _}, ...]" lib
lib/mix/tasks/ex_ast.search.ex:74
  [
    count: :boolean,
    limit: :integer,
    allow_broad: :boolean,
    format: :string,
    json: :boolean,
    expand_imports: :boolean
  ]

1 match(es)

Also collapses a duplicate-match quirk where every literal (lists, tuples, atoms, strings, numbers) was reported twice.

`[...]`, `[1, ...]`, `[first, ..., last]` silently matched nothing:
candidate prefiltering treated the `...` node `{:..., nil, []}` as a call
named `:...`, deriving the signature `{:contains_call, :..., 0}` — a filter
no list can satisfy — so find_all discarded the node before matching. The
matcher itself already handled variable-length lists.

Exclude `...` from signature derivation so any list pattern containing it
yields `:unknown` (a match-all candidate), like `[_, _, _]` already did.
Plain fixed-length list patterns are unchanged.

Also fix a pre-existing duplication surfaced by this: Sourceror wraps
literals in a one-child `__block__` carrying token metadata, and the zipper
visits both it and its inner child, so find_all/find_many matched the same
source twice (every list, tuple, atom, string and number). Skip the bare
inner child when its parent is that one-child block, keeping the wrapper
match whose range spans the brackets/quotes.

Document ellipsis sub-patterns in the README and cover the leading/trailing
capture forms plus the dedup with tests.
@dannote
dannote merged commit 01d57bd into elixir-vibe:master Jul 20, 2026
2 checks passed
@rodrigues
rodrigues deleted the vr/list_ellipsis branch July 21, 2026 00:01
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.

2 participants