Duplicate files intelligently - automatically rename classes, functions, and identifiers to match the new filename. I guess that doesn't make it a duplicate anymore. But when do you ever need the same exact thing twice?
-
Customizable prefixes/suffixes
FirstController→SecondControllerwill replace not just the class name, but also all occurrences ofFirsttoSecond -
Smart word boundaries Short names like
Idwon't accidentally match inside words likevalid -
Plural handling Renaming
Child→Personcorrectly transformsChildren→People. Supports pluralization via conventions and many irregular forms (this does not mean complete coverage of English though) -
Encoding preservation Automatically detects and preserves file encoding (UTF-8, UTF-16, etc.) and BOM markers
-
Easily extensible test suite to verify behavior
Automatically handles variants, even multiple conventions in the same file
- PascalCase
- camelCase
- snake_case
- kebab-case
- space separated
- Title Case
- SCREAMING_SNAKE_CASE
C# Class
Original: BankController.cs
using System.Collections.Generic;
public class Bank
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BankController : IBaseController<Bank>
{
private readonly DbContext _context;
public BankController(DbContext context)
{
_context = context;
}
public Bank GetById(int id)
{
return _context.Set<Bank>().Find(id);
}
public IEnumerable<Bank> GetAll()
{
return _context.Set<Bank>().ToList();
}
public void Create(Bank bank)
{
_context.Set<Bank>().Add(bank);
_context.SaveChanges();
}
public void Update(Bank bank)
{
_context.Set<Bank>().Update(bank);
_context.SaveChanges();
}
public void Delete(Bank bank)
{
_context.Set<Bank>().Remove(bank);
_context.SaveChanges();
}
}
// BankController handles all bank-related operations
// Banks are stored in the databaseAfter duplicating to ProductController.cs
using System.Collections.Generic;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductController : IBaseController<Product>
{
private readonly DbContext _context;
public ProductController(DbContext context)
{
_context = context;
}
public Product GetById(int id)
{
return _context.Set<Product>().Find(id);
}
public IEnumerable<Product> GetAll()
{
return _context.Set<Product>().ToList();
}
public void Create(Product product)
{
_context.Set<Product>().Add(product);
_context.SaveChanges();
}
public void Update(Product product)
{
_context.Set<Product>().Update(product);
_context.SaveChanges();
}
public void Delete(Product product)
{
_context.Set<Product>().Remove(product);
_context.SaveChanges();
}
}
// ProductController handles all product-related operations
// Products are stored in the databaseTypeScript React Component
Original: user-profile.tsx
export interface UserProfileProps {
userId: string;
}
export const UserProfile: React.FC<UserProfileProps> = ({ userId }) => {
return <div className="user-profile">Profile for {userId}</div>;
};
export default UserProfile;After duplicating to admin-profile.tsx
export interface AdminProfileProps {
userId: string;
}
export const AdminProfile: React.FC<AdminProfileProps> = ({ userId }) => {
return <div className="admin-profile">Profile for {userId}</div>;
};
export default AdminProfile;Intelligently converted:
user-profile(kebab-case) →admin-profileUserProfile(PascalCase) →AdminProfileUserProfileProps→AdminProfileProps
Python Class
Original: data_validator.py
class DataValidator:
def __init__(self):
self.name = "DataValidator"
def validate(self, data):
"""DataValidator implementation"""
return True
# creates the data validator
def create_data_validator():
return DataValidator()After duplicating to schema_validator.py
class SchemaValidator:
def __init__(self):
self.name = "SchemaValidator"
def validate(self, data):
"""SchemaValidator implementation"""
return True
# creates the schema validator
def create_schema_validator():
return SchemaValidator()All references including snake_case function names and comments updated automatically.
- Right-click on a file in the Explorer (or in the editor)
- Select Smart Duplicate (
Ctrl+K Ctrl+D) - Enter the new filename
- Review the diff
- Explorer context menu - Right-click any file
- Editor context menu - Right-click in an open file
- Command Palette - Search for "Smart Duplicate"
| Setting | Default | Description |
|---|---|---|
smartDuplicateFile.postDuplicationAction |
compareWithOriginal |
Action after duplicating: openFile, compareWithOriginal (diff view), or openSideBySide |
smartDuplicateFile.strippablePrefixes |
["use", "get", "set", "is", "has"] |
Prefixes to strip when replacing (e.g., useBankAccount → useCurrency replaces BankAccount with Currency) |
smartDuplicateFile.strippableSuffixes |
["Store", "Service", ...] |
Suffixes to strip when replacing (e.g., UserStore → ProductStore replaces User with Product) |
Access settings via Settings (Ctrl+,) → search "Smart Duplicate File".
- Use the default diff view to review changes before saving
- Update
strippablePrefixesandstrippableSuffixeswith ones common in your codebase - Works best with consistent naming conventions
💡 A note on clean code: Many replacements this extension performs (like updating comments that repeat class names) wouldn't be necessary with cleaner code practices. Comments like
// creates the user validatoror"""UserValidator implementation"""are often redundant - the code should be self-documenting. If you find yourself needing extensive comment replacements, consider whether they add value in the first place.
Contributions are welcome! Here's how to get started:
git clone https://github.com/FreHu/vscode-duplicate-plus-plus.git
cd vscode-duplicate-plus-plus
npm installPress F5 in VS Code to launch the Extension Development Host. This may show some errors because there are invalid .ts files used as test data. They can be ignored.
npm testEach test is composed of an input/output file pair in src/test/fixtures/:
To add a new test case:
- Create
your-test-name.in.extwith the input content - Create
your-test-name.out.extwith the expected output - Add the test case to
src/test/extension.test.ts
None, but this simply can't cover everything. If you feel like you found
- a replacement which seems doable but currently isn't handled
- a replacement which shouldn't occur
Then open an issue and include a test case.
Initial release:
- Intelligent multi-convention filename replacement
- Configurable prefix/suffix stripping
- Diff view for reviewing changes
- Context menu integration (Explorer & Editor)