Rusty Refactor
A powerful VSCode extension for extracting Rust code into modules with intelligent type detection, impl block preservation, and RustyRoad-aware file navigation.
✨ Features
💬 Copilot Chat Integration (Enhanced!)
- Three Language Model Tools optimized for zero-work LLM consumption:
rustyRefactor_refactor_file: Orchestration tool that refactors entire files in one command
rustyRefactor_extract_to_module: Extract specific code to a new module
rustyRefactor_analyze_rust_code: Analyze code structure and dependencies
- Structured JSON Outputs: All tools return 100% structured data - no parsing needed!
- Success/failure indicators, recommended actions, extracted items
- Actionable error messages with specific fixes
- Complete import statements and usage examples
- Intelligent Import Suggestions: Name resolution engine with confidence scores
- 50+ std library items, external crates (serde, tokio, clap)
- Edit distance matching for fuzzy search
- Returns JSON:
{"type_name": "HashMap", "import_path": "std::collections::HashMap", "confidence": 1.0}
- Incremental Cache System: Salsa-style caching for instant repeat analysis
- Zstd compression, SHA-256 keys, LRU cleanup
- Logs: "⚡ Cache HIT! (Hit rate: 85.3%)"
- Auto-Routing: Automatically routes symbols to RustyRoad conventions
- Data models →
src/models/
- Services →
src/services/
- Utilities →
src/utils/
- Agent Mode Support: Copilot can chain multiple operations automatically
- Confirmation Dialogs: User-friendly confirmations before any refactoring operations
🤖 AI-Powered Documentation with Multi-Layer Validation
- Automatic Documentation Generation: Uses GPT-4o-mini to generate comprehensive Rust documentation
- LLM-as-a-Judge Validation: AI validates its own output to catch errors before applying changes
- Rust-Analyzer Integration: Validates generated code compiles and symbols are properly detected
- Smart Retry Logic: Automatically retries with corrective feedback when validation fails
- Smart Summaries: AI-generated extraction summaries in the original file
- Copilot Integration: Leverages GitHub Copilot's language models
- Comprehensive Output Logging: View validation steps in the "Rusty Refactor" output panel
- Configurable: Can be set to always ask, always generate, or never generate
- Intelligent Import Analysis: Only copies imports that are actually used in the extracted code
- Rust-Analyzer Cleanup: Automatically removes unused imports after extraction using LSP diagnostics
- Implementation Block Preservation: Automatically wraps extracted methods in
impl blocks when extracting from implementations
- Type Detection: Identifies types, traits, and dependencies in your selected code
- Automatic Module Registration: Finds parent module and properly registers with
pub mod and pub use
📁 RustyRoad Integration
Built specifically for RustyRoad projects with Rails-like conventions:
- Auto-suggests conventional directories:
controllers, models, views, services, middleware, etc.
- Browse project structure: Intuitive directory navigation with QuickPick
- Create directories on-the-fly: Automatically creates suggested RustyRoad directories
Extract with File Browser ⭐ (Recommended)
- Visual directory browsing
- RustyRoad directory suggestions
- Create new directories as needed
Quick Extract
- Uses default path from settings
- Fastest for standard workflows
Custom Path Extract
- Type exact file path manually
- For advanced users
💬 Using with Copilot Chat
Rusty Refactor provides three Language Model Tools in Copilot Chat, all optimized for zero-work LLM consumption with 100% structured JSON outputs!
The most powerful way to refactor - let the tool handle everything:
@workspace refactor examples/sample.rs
This single command will:
- Analyze the entire file to find all extractable symbols
- Auto-route each symbol to the right place (models/, services/, utils/)
- Extract each symbol to its own module
- Suggest missing imports with confidence scores
- Return complete refactoring plan with step-by-step log
Example output:
{
"success": true,
"extracted_modules": [
{"module_name": "user", "module_path": "src/models/user.rs", "symbols": ["User"]},
{"module_name": "database", "module_path": "src/services/database.rs", "symbols": ["Database", "Connection"]}
],
"suggested_imports": [
{"type_name": "HashMap", "import_path": "std::collections::HashMap", "confidence": 1.0}
],
"summary": "✓ Refactoring complete: 8/8 steps successful"
}
For precise control, extract specific symbols:
@workspace extract User struct from examples/sample.rs to src/models
Structured output:
{
"success": true,
"module_name": "user",
"extracted_items": {"functions": ["new", "display"], "structs": ["User"]},
"usage": "use crate::user::*;"
}
Analyze Before Refactoring
Understand code structure first:
@workspace analyze examples/sample.rs
Structured output:
{
"extractable": true,
"recommended_action": "extract_to_module with functionName: 'new' for symbol-based extraction",
"functions": [{"name": "new", "visibility": "pub"}],
"structs": [{"name": "User", "field_count": 3}]
}
Why Use Copilot Chat?
- Zero-Work for LLMs: 100% structured JSON outputs - no parsing required
- Intelligent Routing: Automatically follows RustyRoad conventions
- Actionable Errors: "Use lowercase with underscores (e.g., 'user_service')"
- Complete Metadata: Import statements, confidence scores, execution logs
- Natural Language: Describe what you want in plain English
- Automated: Let Copilot handle the entire refactoring workflow
- Fast: Incremental cache system speeds up repeated analysis
- Smart Imports: Name resolution engine suggests missing imports
📖 Usage Examples
Before - Original file src/models/email.rs:
use super::Email;
use rustyroad::database::{Database, PoolConnection};
impl Email {
pub fn new(
to_field: String,
from_field: String,
subject: String,
body: String,
) -> Self {
Self {
id: 0,
to_field,
from_field,
subject,
body,
status: "pending".to_string(),
}
}
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
let sql = r#"
INSERT INTO emails (to_field, from_field, status)
VALUES ($1, $2, $3)
RETURNING *
"#;
let database = Database::get_database_from_rustyroad_toml().unwrap();
let pool = Database::get_db_pool(database).await.unwrap();
sqlx::query_as::<_, Self>(sql)
.bind(&email.to_field)
.bind(&email.from_field)
.bind(&email.status)
.fetch_one(&pool)
.await
}
}
Steps:
- Select the
create method (just the method, not the entire impl block)
- Right-click → "Rusty Refactor: Extract to Module (Browse Files)"
- Enter module name:
email_repository
- Browse to:
src/models/repositories/ or create it
- Confirm
After - New file src/models/repositories/email_repository.rs:
//! Email repository module
//!
//! This module was automatically extracted by Rusty Refactor.
// Imports from original file
use super::Email;
use rustyroad::database::{Database, PoolConnection};
impl Email {
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
let sql = r#"
INSERT INTO emails (to_field, from_field, status)
VALUES ($1, $2, $3)
RETURNING *
"#;
let database = Database::get_database_from_rustyroad_toml().unwrap();
let pool = Database::get_db_pool(database).await.unwrap();
sqlx::query_as::<_, Self>(sql)
.bind(&email.to_field)
.bind(&email.from_field)
.bind(&email.status)
.fetch_one(&pool)
.await
}
}
Updated - Original file src/models/email.rs:
use super::Email;
use rustyroad::database::{Database, PoolConnection};
pub mod repositories;
pub use repositories::email_repository::*;
impl Email {
pub fn new(
to_field: String,
from_field: String,
subject: String,
body: String,
) -> Self {
Self {
id: 0,
to_field,
from_field,
subject,
body,
status: "pending".to_string(),
}
}
// Code extracted to src/models/repositories/email_repository.rs
// Available as: email_repository::*
}
Updated - Parent module file src/models/mod.rs or src/lib.rs:
pub mod repositories;
pub use repositories::*;
Example 2: Extracting Standalone Functions
Before:
use std::collections::HashMap;
pub fn calculate_total(items: &[Item]) -> f64 {
items.iter().map(|i| i.price).sum()
}
pub fn apply_discount(total: f64, discount: f64) -> f64 {
total * (1.0 - discount)
}
Steps:
- Select both functions
- Extract to
src/utils/pricing.rs
After - src/utils/pricing.rs:
//! Pricing module
// Imports from original file
use std::collections::HashMap;
pub fn calculate_total(items: &[Item]) -> f64 {
items.iter().map(|i| i.price).sum()
}
pub fn apply_discount(total: f64, discount: f64) -> f64 {
total * (1.0 - discount)
}
🎨 How It Works
Import Handling
Rusty Refactor intelligently copies all imports from your original file to the new module. This means:
- ✅ External crate imports are preserved
- ✅ Local module imports are maintained
- ✅ No more compiler errors from missing imports
- ✅ Only adds
use super::* for types defined in the parent module (like struct definitions for impl blocks)
Implementation Block Detection
When you select code inside an impl block:
- Detects you're inside an
impl Email or impl Trait for Type
- Automatically wraps extracted code in the same impl structure
- Imports the target type from parent module if needed
- Preserves trait implementations correctly
Module Registration
The extension automatically:
- Finds the correct parent module file (
mod.rs, lib.rs, or main.rs)
- Adds
pub mod module_name; with proper path attributes if needed
- Adds
pub use module_name::*; to re-export public items
- Handles nested module structures
⚙️ Configuration
{
// Enable RustyRoad conventions (controllers, models, etc.)
"rustyRefactor.rustyRoadMode": true,
// Default path for quick extract
"rustyRefactor.defaultModulePath": "src",
// Auto-format after refactoring
"rustyRefactor.autoFormatAfterRefactor": true,
// Add doc comments to extracted modules
"rustyRefactor.addModuleDocComments": true,
// Integrate with rust-analyzer
"rustyRefactor.integrationWithRustAnalyzer": true,
// AI Documentation (requires GitHub Copilot)
"rustyRefactor.aiAutoDocumentation": false, // Set to true to always generate
"rustyRefactor.aiAskEachTime": true, // Ask before generating each time
// AI Model Selection (format: "vendor/family")
"rustyRefactor.aiPreferredModel": "copilot/gpt-4o",
// Maximum directory depth for file search
"rustyRefactor.searchDepth": 5
}
AI Documentation Setup
- Install GitHub Copilot: The AI documentation feature requires an active GitHub Copilot subscription
- Enable in Settings: Set
rustyRefactor.aiAutoDocumentation to true for automatic generation
- Or Choose Each Time: Keep
rustyRefactor.aiAskEachTime as true to be prompted each extraction
AI Validation Pipeline
The extension uses a multi-layered validation system to ensure AI-generated documentation is correct:
1. LLM Judge - AI reviews its own output for 8+ criteria:
- Code preservation (no modifications to original code)
- Valid doc comment syntax (
/// and //!)
- No inline documentation
- No commented-out code
- Complete code presence
- Balanced braces
- No
#[doc] attributes
- Proper structure
2. Rust-Analyzer Validation - Creates temporary file and checks:
- Compilation errors via VS Code diagnostics
- Symbol detection via symbol provider API
- All original symbols (functions, structs, etc.) are present
3. Smart Retry - If validation fails:
- Retries once with corrective feedback
- Uses specific error messages to guide retry
- Falls back to original code if retry also fails
4. Output Logging - View detailed validation in Output panel:
- Open View → Output
- Select "Rusty Refactor" from dropdown
- See validation steps, errors, and retry attempts
AI Documentation Example:
Before extraction, your code might look like this:
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
let sql = r#"INSERT INTO emails..."#;
// ... implementation
}
After AI documentation generation:
/// Creates a new email record in the database
///
/// This function inserts a new email entry into the emails table with the provided
/// email data and returns the created record with all database-generated fields.
///
/// # Arguments
///
/// * `email` - The email data to be inserted
///
/// # Returns
///
/// Returns `Result<Self, sqlx::Error>` containing the created email record with
/// database-generated fields populated, or an error if the insertion fails.
///
/// # Errors
///
/// This function will return an error if:
/// * Database connection fails
/// * SQL query execution fails
/// * Email data validation fails
///
/// # Example
///
/// ```rust
/// let email = Email::new(...);
/// let created = Email::create(email).await?;
/// ```
pub async fn create(email: Email) -> Result<Self, sqlx::Error> {
let sql = r#"INSERT INTO emails..."#;
// ... implementation
}
🚦 Commands
| Command |
Description |
Shortcut |
Rusty Refactor: Extract to Module (Browse Files) |
Browse and select destination directory |
Right-click menu |
Rusty Refactor: Extract to Module |
Quick extract to default path |
Right-click menu |
Rusty Refactor: Extract to Module (Custom Path) |
Manually type file path |
Right-click menu |
📋 Requirements
- VSCode 1.80.0 or higher
- Rust project with
Cargo.toml
- rust-analyzer extension (recommended for best results)
🔧 Installation
- Install from VSCode Marketplace: Search for "Rusty Refactor"
- Or install from VSIX:
code --install-extension rusty-refactor-0.2.0.vsix
🎯 RustyRoad Project Structure
This extension works great with RustyRoad's Rails-inspired structure:
src/
├── controllers/ # HTTP request handlers
├── models/ # Data models and business logic
│ └── repositories/ # Database access layer
├── views/ # Template rendering
├── services/ # Business logic services
├── middleware/ # Request/response middleware
├── helpers/ # Helper functions
├── utils/ # Utility functions
├── config/ # Configuration
├── routes/ # Route definitions
└── lib.rs or main.rs
When you use "Extract with File Browser", these directories are suggested automatically!
🐛 Troubleshooting
AI Documentation Issues
- No documentation generated: Check the "Rusty Refactor" output panel for validation errors
- Code appears broken: The validation pipeline should prevent this - report as a bug with console output
- Validation keeps failing: Try with simpler code first, or disable AI documentation temporarily
- See validation logs: View → Output → Select "Rusty Refactor" from dropdown
Imports not working?
- The extension copies all imports from your original file
- If you still get errors, check that the types are accessible from the new module location
- You may need to adjust paths if moving between different module hierarchies
Module not registered in parent?
- Check that your project has a proper module structure (
mod.rs, lib.rs, or main.rs)
- The extension looks for parent modules automatically
- You can manually add
pub mod module_name; if needed
Impl block not preserved?
- Make sure you're selecting code from inside an impl block
- The extension detects the impl context automatically
- If issues persist, try selecting the entire impl block
🤝 Contributing
Contributions welcome! Please open issues or PRs on GitHub.
📄 License
MIT License - See LICENSE file for details
🙏 Acknowledgments
- Built for the RustyRoad framework
- Inspired by Rails conventions and Ruby refactoring tools
- Powered by rust-analyzer LSP integration
Happy Refactoring! 🦀✨