I struggled with this one a lot. My first version of the code was not idiomatic at all. It is too verbose and relies a lot on Enum.at and elem. That's in comparison to using pattern matching.
In my second iteration I've borrowed some improvements from others. Namely, checking if the expression is a function with a in [:def, :defp] which I was checking using a conditional statement before.
But I still decided to keep my code instead of using pattern matching. Because of the complexity of the AST data structure, the pattern matching is rather complicated and I feel it's not as readable. Despite it being shorter. But this is really just a preference. Most consider the solutions that rely on pattern matching heavily more elegant.
I have also learned about mix_test_watch | Hex and I have started using it. It helps me run all tests on every save. I install the dependency:
I have VSCode and iTerm side by side and I work on my code. On every save, the tests run in iTerm. I have reduced using iex dramatically as I am adding IO.inspect statements that immediatelly show up in iTerm after each save.