Vale CMS Need more than a rule playground? Explore
Why Vale

Your comments are documentation too

Code

Most of what a developer reads about your software is written inside the software. Vale treats comments and docstrings as first-class prose: it parses the source with a real grammar, pulls the prose out, lints it, and reports every alert at its line and column in the original file.

Source code reference

A grammar draws the boundary

Vale parses the file with tree-sitter and runs a query against the parse tree. A comment is whatever the language's own grammar calls a comment—which is why a delimiter inside a string literal stays where it belongs.

store.go
package store
// Get returns the record for id.
//
// It can utilize the cache when possible.
func Get(id string) (*Record, error) {
key := "// not a comment"
return s.lookup(key + id)
}
Query
(comment) @comment

One query covers line and block comments alike—the grammar already knows the difference.

Highlighted lines are handed to your rules. Everything dimmed is code—including the string literals that contain comment delimiters, which is exactly the case a pattern-matching extractor gets wrong.

The parsing is tree-sitter, through the Go bindings, with one grammar per language and a query naming the nodes to collect. It is the same machinery your editor uses to highlight the file.

Markdown, inside a comment

Doc comments are rarely plain text. Associate a markup format with a file extension and Vale parses the comment body with that format's parser, so everything on the markup page applies inside your source files.

.vale.ini
[formats]
# Rust source, Markdown comments
rs = md
[*.{rs,md}]
BasedOnStyles = Vale, Microsoft
  1. 1

    Find

    tree-sitter locates every comment in the source file.

  2. 2

    Undecorate

    Per-line markers come off, and the body is dedented.

  3. 3

    Parse

    The remaining text goes through the markup format's own parser.

  4. 4

    Map back

    Each alert is reported at its position in the source file.

Once a format is associated, its features come with it—including TokenIgnores and BlockIgnores, which are otherwise unavailable in source code because they work by wrapping a match in the format's code delimiter.

Decoration is not markup

A C-style block comment decorates every line with an asterisk. Markdown reads that asterisk as a list marker. Vale removes the decoration first, so a comment parses as what its author meant.

Source
/**
* Reads the record and returns it.
*
* Pass `refresh` to bypass the cache:
*
* * `refresh: true` re-reads from disk.
* * `refresh: false` uses the cache.
*/
What the parser gets
Reads the record and returns it.
Pass `refresh` to bypass the cache:
* `refresh: true` re-reads from disk.
* `refresh: false` uses the cache.

A paragraph followed by a two-item list—not one seven-item list, which is what the decoration would otherwise produce.

An asterisk counts as decoration only when whitespace or the end of the line follows it, so a line that starts *emphasis* keeps its markup. Relative indentation survives too, which is what makes an indented fenced block inside a comment still read as code.

Requires Vale v3.17.0 or later.

Beyond comments

Comments are where Vale looks by default. A View replaces that with a query of your own, so the string literals a user reads, or the docstrings a grammar can name, are prose too.

Languages

Fourteen have a tree-sitter grammar built in.

GoRustPythonRubyJavaScriptTypeScriptTSXJavaCC++JuliaProtobufCSSYAML

The rest are handled by delimiter scanning—still comment-aware, but without a parse tree behind it:

C#HaskellLESSLuaPerlPHPPowerShellRSassScalaSwift