There’s no need to manually iterate through the tree, and use if statements to select nodes. Instead you can just write a couple of simple queries (and even use treesitters web UI to test the queries), and have the treesitter just provide all the nodes for you.
https://tree-sitter.github.io/tree-sitter/using-parsers#patt...
This is trivial with codegen.com. Syntax below:
# Iterate through all files in the codebase
for file in codebase.files:
# Check for functions with the pytest.fixture decorator
for function in file.functions:
if any(d.name == "fixture" for d in function.decorators):
# Rename the 'db' parameter to 'database'
db_param = function.get_parameter("db")
if db_param:
db_param.set_name("database")
# Log the modification
print(f"Modified {function.name}")
Live example: https://www.codegen.sh/codemod/4697/public/diff
- perl -pi -e 's/foo/bar/g' files
"-pi" means "in place edit" so it will change the files in place. If you have a purely mechanical change like he's doing here it's a very reasonable choice. If you're not as much of a cowboy as I am, you can specify a suffix and it will back the files up, so something like
perl -p -i.bak -e 's/db/database/g' py
For example then all your original '.py' files will be copied to '.py.bak' and the new renamed versions will be '.py'
For vim users (I know emacs has the same thing but I don't remember the exact invocation because it has been >20years since I used emacs as my main editor) it's worth knowing the "global" command. So you can execute a particular command only on lines that match some regex. So say you want to delete all the lines which mention cheese
:%g/cheese/d
Say you want to replace "db" with "database" but only on lines which start with "def"
:%g/^def/s/db/database/
OK cool. Now if you go 'vim *py' you can do ":argdo g/^def/s/db/database/ | update" and it will perform that global command across all the files in the arg list and save the ones which have changed.
I guess what I’m looking for is something that
* can carry out the kind of refactorings usually reserved for an IDE
* has a concise language for representing the refactorings so new ones can be built quite easily
* can apply the same refactoring in multiple places, with some kind of search language (addressing the task of renaming a test parameter in multiple methods)
* ideally does this across multiple programming languages
If you're looking for a higher level interface, GritQL[0] is built on top of tree-sitter and could handle the same refactor with this query:
language python
`def $_($_): $_` as $func where $func <: contains `database` => `db`
[0] https://github.com/getgrit/gritql
pattern: |-
def $FUNC(..., database, ...):
$...BODY
fix: |-
def $FUNC(..., db, ...):
$...BODY
Here are two approaches in Emacs.
https://emacs.stackexchange.com/a/69571
https://rigsomelight.com/2010/02/14/emacs-interactively-find...
there's tsmod at least https://github.com/WolkSoftware/tsmod
i've heard of fastmod, codemod but never used them.
An input like
is turned into which seems roughly like what the author is after. Mentions of "database" outside function definitions are not modified. That sort of logic I always found hard to replicate in basic GNU-like tools. If run without stdin, the above command runs recursively, in-place (careful with that one!).Note: I just wrote this, and version 0.13.2 is required for the above to work.
[0]: https://github.com/alexpovel/srgn