How to Automate Code Refinement Using 'sg' (with examples)
Ast-grep, commonly referred to as ‘sg,’ is a powerful command-line tool designed for code structural search, linting, and rewriting. This utility is essential for developers looking to enforce coding standards, automate code transformations, or simply search through large codebases efficiently. By leveraging abstract syntax trees (AST), ‘sg’ allows precise code manipulation beyond what regular text-based searches can achieve. The examples below illustrate different ways to utilize ‘sg’ for various code management tasks, showcasing its versatility and efficiency in real-world scenarios.
Use case 1: Scan for possible queries using interactive mode
Code:
sg scan --interactive
Motivation:
The interactive mode of ‘sg’ is incredibly useful for developers who are exploring a codebase and seeking to uncover potential queries for further analysis. By using this mode, developers can dynamically generate, modify, and test search queries against their code without leaving the command line. This hands-on approach is especially beneficial for those new to the codebase or when dealing with unfamiliar coding patterns.
Explanation:
sg
: This is the command-line tool Ast-grep, which operates primarily on abstract syntax trees.scan
: This subcommand initiates a search through the codebase to identify patterns or constructs based on specified queries.--interactive
: This option launches an interactive session, allowing users to iteratively build and refine their search queries. Through a series of prompts and feedbacks, users can explore various query results and refine their search criteria on the fly.
Example Output:
When the interactive mode is initiated, you’ll be presented with a series of prompts where you can type or modify your search queries. A live query editor helps you adjust and immediately see the results of your search, with interactive suggestions and feedback as you type.
Use case 2: Rewrite code in the current directory using patterns
Code:
sg run --pattern 'foo' --rewrite 'bar' --lang python
Motivation:
Rewriting code using specified patterns can drastically reduce maintenance time, particularly for repetitive tasks like refactoring code across large projects. In this example, we’re replacing a function, variable, or keyword ‘foo’ with ‘bar’ across a Python codebase. This is useful when updates to the code style guide necessitate such changes or when a library update changes function names.
Explanation:
sg run
: This command activates the rewrite operation in Ast-grep.--pattern 'foo'
: This argument specifies the pattern to match in the code. Here ‘foo’ represents the identifier or pattern we want to find.--rewrite 'bar'
: This specifies the replacement pattern. ‘bar’ is what ‘foo’ will be replaced with across the code.--lang python
: This restricts the operation to Python files, ensuring that only relevant source files are altered, thus preventing accidental changes to other languages in a polyglot codebase.
Example Output:
The output of the command will list each instance of ‘foo’ found, along with the file and line number, followed by the updated use of ‘bar’. It provides a summary of all changes applied without modifying the actual files unless confirmed by the user.
Use case 3: Visualize possible changes without applying them
Code:
sg run --pattern 'useState<number>($A)' --rewrite 'useState($A)' --lang typescript
Motivation:
Sometimes developers need to visualize what changes would occur if a certain refactoring were applied across the codebase, without actually making those changes immediately. This use case is particularly useful for reviewing the scope and impact of a change and ensuring it aligns with the developer’s expectations.
Explanation:
sg run
: Initiates a search operation with a potential for rewriting.--pattern 'useState<number>($A)'
: This AST pattern matches instances of generic TypeScript usage ofuseState
with a type parameter ofnumber
.--rewrite 'useState($A)'
: This rewrites the pattern to exclude the TypeScript generic, allowing you to standardize the hook usage without specific typing.--lang typescript
: Ensures that this operation is confined to TypeScript files.
Example Output:
The tool produces a diff-like output that shows both the original and the proposed changes. The lines where useState<number>
is used are paired with their intended rewrite, useState
, allowing the developer to review all potential modifications.
Use case 4: Output results as JSON, extract information using jq
and interactively view it using jless
Code:
sg run --pattern 'Some($A)' --rewrite 'None' --json | jq '.[].replacement' | jless
Motivation:
For deeper insights and complex transformations, outputting the results as JSON allows for advanced processing and analysis. Tools like jq
enable easy manipulation and extraction of specific fields from JSON data, while jless
provides a smooth interactive viewing experience.
Explanation:
sg run
: Executes a search with a pending rewrite operation.--pattern 'Some($A)'
: This identifies patterns in the code where the wrapperSome
is applied.--rewrite 'None'
: Suggests replacing these instances withNone
, which may be required due to changes in logic or library semantics.--json
: Outputs the results in JSON format for further downstream processing.| jq '.[].replacement'
: Pipes the JSON output tojq
, filtering out the specific replacement fields we’re interested in.| jless
: Finally sends the processed output tojless
for interactive exploration, giving users the ability to navigate through the extracted data easily.
Example Output:
The JSON output details the potential changes, with each entry showing the original and the proposed state. Using jq
and jless
, users can focus on the parts of the data that matter most, such as counts and specific instances, all presented within an interactive interface.
Conclusion
Ast-grep offers a suite of sophisticated tools tailored for developers seeking to optimize their workflow through advanced code querying, refactoring, and analysis. Whether you are combing through a fresh codebase, ensuring adherence to new coding conventions, or modeling quick refactoring changes before final application, ‘sg’ caters to a broad array of practical needs. Through its diverse command options and flexibility, ‘sg’ empowers developers to maintain cleaner and more consistent codebases with remarkable precision and efficiency.