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 == $Amatchesx == xand does not matchx == 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 topath. A bare*.tsapplies at any depth.lang: Forces a language instead of per-file detection.mode:matches, the default, returns each match with its code and captures.countreturns matches per file and a total.limitandoffset: Page the results.limitdefaults to 50 and clamps at 200.
Search for a syntax pattern
To search for a syntax pattern, follow these steps:
- Call
AstSearchwithpattern, addingpath,globorlangwhen 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:
- Call
AstEditwithpatternandrewrite, addingpath,globorlangwhen the edit is narrower than the working directory, and leavingapplyunset.
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:
- Call
AstEditwith the same pattern, rewrite and scope, addingapply: trueand the returnedplan. - 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 doctornames 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.
Related pages
Edits and change transactions, Language servers, Workshop