Mercury CLI
Documentation

Code

Search and edit by syntax

Mercury's AstSearch and AstEdit tools match code against parsed syntax trees, never against text. AstSearch finds matching nodes, and AstEdit rewrites them. Both use one matcher, so an edit rewrites exactly what the search finds.

Pattern elements

  • Complete node: A pattern is one complete node of the target language, written as code. A fragment that cannot stand alone is wrapped in its container, such as class $_ { $$$BODY }.
  • $NAME: Matches exactly one node and captures it.
  • $$$NAME: Matches a sequence of zero or more sibling nodes and captures it.
  • $_: Matches one node without capturing it.
  • $$$: Matches a sequence without capturing it.
  • Repeated name: Every occurrence must match identical code. $A == $A matches x == x and does not match x == y.

Names use uppercase letters, digits and underscores. $$X and $$$name are literal text. Spacing, line breaks and comments do not affect a match. Node types must agree, sequences line up with the $$$ meta-variables, and leaves compare exact text.

A pattern that does not parse is refused with the parser's location and a corrected example.

Search parameters

  • pattern: The code pattern.
  • path: A file or a directory. The default is the working directory.
  • glob: A filter relative to path. A bare *.ts applies at any depth.
  • lang: Forces a language instead of per-file detection.
  • mode: matches, the default, returns each match with its code and captures. count returns matches per file and a total.
  • limit and offset: Page the results. limit defaults to 50 and clamps at 200.

Search for a syntax pattern

To search for a syntax pattern, follow these steps:

  1. Call AstSearch with pattern, adding path, glob or lang when the search is narrower than the working directory.

The tool returns every match with its file, range, node type, source text and captures.

Every result names what was not searched: files that did not parse, languages in which the pattern does not parse, extensions without a grammar, read-denied files and any bound reached. A result with zero matches names the pattern and that census. A search is subject to read permission over the scope path.

Preview a structural edit

To preview a structural edit, follow these steps:

  1. Call AstEdit with pattern and rewrite, adding path, glob or lang when the edit is narrower than the working directory, and leaving apply unset.

The tool returns a unified diff per file and a plan token beginning ae-. Nothing is written, and the dry run asks for no permission.

The rewrite is code in the target language. $NAME and $$$NAME insert the captured source verbatim. An empty rewrite, "", deletes the matched node, and a node that owns its line takes the line with it. When the rewrite has the same shape as the pattern and only leaf tokens differ, it lands as token edits inside the matched node, so a rename keeps the node's body, indentation and comments. Other rewrites substitute the template literally.

Apply a structural edit

To apply a structural edit, follow these steps:

  1. Call AstEdit with the same pattern, rewrite and scope, adding apply: true and the returned plan.
  2. Approve the aggregate permission request, which names the files, if one appears.

The tool re-plans over the current file contents, writes the planned edits, and returns a fresh anchor per file and one change receipt.

The apply refuses when the token no longer matches because a file, the pattern, the rewrite or the scope changed, and offers the current dry run instead. A denied path refuses the whole set, and nothing is written. Each written file gets a file-history snapshot, so /rewind restores it.

These cases are refused by name with nothing written: a match nested inside another match, a rewrite naming a meta-variable the pattern does not capture, an anonymous $$$ or $_ in the rewrite, a rewrite that would leave a file unparsable, and more than 100 files or 500 matches in one edit. Matches already in the rewritten form are counted and left alone.

Language availability

The registry routes 23 languages, and the tool descriptions list exactly the grammars found in the running build.

  • Every build: python, go, rust, javascript, typescript, tsx, bash, c-sharp, cpp, css, java, php, ruby, powershell, ini and regex.
  • Release archives also: c, html, json, toml, kotlin, swift and vue. A source build without them says so, and mercury doctor names the gap.

Detection is per file, by basename first and then by extension. lang forces a language and accepts the spellings ts, js, py, rs, cs, c++, sh, rb, kt and ps1.

Files of a language whose grammar the build lacks are skipped and counted, with the remedy named in the result. A single such file, or a lang pin on it, is refused by name with the remedy. A file whose extension routes to no grammar is skipped and counted, never text-matched. MERCURY_STRUCTURE_POLYGLOT=0 removes both tools from the catalogue.

Edits and change transactions, Language servers, Workshop