Skip to content

feat: add Elixir comment and doc-attribute extraction - #1151

Open
C-Sinclair wants to merge 1 commit into
vale-cli:v3from
C-Sinclair:feat/elixir-comments
Open

feat: add Elixir comment and doc-attribute extraction#1151
C-Sinclair wants to merge 1 commit into
vale-cli:v3from
C-Sinclair:feat/elixir-comments

Conversation

@C-Sinclair

Copy link
Copy Markdown

Adds Elixir (.ex, .exs) to the tree-sitter comment extractors.

Why the doc attributes, not just the comments

Elixir has no documentation comment syntax. Its published API documentation lives in module attributes holding a string or a heredoc:

defmodule Session do
  @moduledoc """
  A scheduled period of care delivery.
  """

  @doc "Books a session for a client."
  def book(client), do: ...
end

That is what mix docs renders and what a reader of a module reads first, so extracting only # comments would see the asides and none of the documentation.

Both are extracted. Comments keep the usual scopes; the attributes get a doc meta scope, so text.comment.doc.block can be held to a different standard than an implementation note, or excluded on its own via IgnoredScopes. @moduledoc, @doc, @typedoc and @shortdoc are covered; @doc false and @doc since: "1.0.0" carry no prose and the queries require a string or sigil argument, so neither matches.

Predicate-only captures

The attribute name is what decides whether a node is documentation, and the prose is a different node — so the query has to test one node and capture another. #match? can only test a captured node, so this needs a capture that is tested but not linted.

query.go now skips captures whose name begins with _, which is the convention tree-sitter itself uses for internal captures. Without it, the query has to capture the whole @moduledoc """...""" and take the delimiters off with Delims — which for the single-quoted @doc "..." form would also strip any quote written inside the prose.

With it, the queries capture quoted_content (the body of the string) and the extracted text needs no delimiter stripping at all. Delims is just #.

This is a small behaviour change to shared code: any existing query using an _-prefixed capture name would stop having that capture linted. I could find none in-tree, and linting a capture that exists for a predicate looks like the unintended reading, but flag it in case you'd rather have it behind a language flag.

Notes

  • github.com/smacker/go-tree-sitter/elixir is already vendored, so this adds no dependency.
  • .heex (Phoenix HTML templates) is deliberately left alone — no grammar for it in the set, and mapping it onto HTML would reduce it to <!-- --> comments.

Tests

  • testdata/comments/{in,out}/8.ex, 9.exs — extraction fixtures covering heredoc docs, the single-quoted form, sigil docs (~S"""), a line-comment run, a trailing comment, and the prose-free attributes.
  • testdata/e2e/lint.yaml — an elixir case against testdata/fixtures/formats/test.ex; all seven reported columns are exact.
  • TestPredicateOnlyCapture and TestDocAttributesWithoutProse in comments_test.go.

go test ./internal/... is green.

Happy to split the query.go change into its own commit or PR if you'd prefer to take them separately.

Elixir has no documentation comment syntax. Its published API
documentation lives in `@moduledoc`, `@doc`, `@typedoc` and
`@shortdoc` attributes holding a string or heredoc -- that is what
`mix docs` renders and what a reader of a module reads first -- so a
comment-only pass would see the asides and none of the documentation.

Both are extracted. Comments keep the usual `text.comment.line` and
`text.comment.block` scopes; the attributes get a `doc` meta scope,
so `text.comment.doc.block` can be held to a different standard, or
excluded, independently of an implementation note.

The queries capture `quoted_content` -- the body of the string --
rather than the string itself, which leaves the delimiters out without
a Delims pattern having to remove them. That matters for the
single-quoted `@doc "..."` form, where stripping the delimiter by
regex would also strip any quote written inside the prose.

The attribute name is what decides whether a node is documentation, and
the prose is a different node, so the query needs to test one node and
capture another. Query captures named with a leading underscore are now
skipped rather than linted, which is the convention tree-sitter itself
uses for internal captures; without it the only testable node is the
one you extract.

`@doc false` and `@doc since: "1.0.0"` carry no prose, and the
queries require a string or sigil argument, so neither matches.

The grammar is already vendored in go-tree-sitter, so this adds no
dependency.
@C-Sinclair
C-Sinclair marked this pull request as ready for review August 21, 2026 12:29
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.

1 participant