Refactor Engine Module
July 8, 2026 · View on GitHub
This package powers GML-native codemods and semantic refactoring transactions, as outlined in the target-state architecture plan. It implements a native, GML-centric Collection API (inspired by jscodeshift) to handle atomic cross-file edits, metadata updates (.yy, .yyp), and structural migrations.
Ownership Boundaries
@gmloop/refactor is the owner of Global Transactions (Codemods).
- Depends on
@gmloop/semanticfor symbol/scope analysis inputs. - Owns atomic cross-file edits, metadata updates, and structural migrations.
- Implements a jscodeshift-like Collection API for GML ASTs.
- Is the ONLY layer that should decide whether a rename requires cross-file edits or metadata changes.
- Codemod/fixer commands are responsible for repairing non-parsable source text to restore parsability.
It does not replace lint or formatter domains:
@gmloop/lintowns Diagnostic Reporting and Local Repairs (single-file fixes). Lint rule autofixes are responsible for fixing valid-but-forbidden syntax (e.g., style violations or deprecated patterns that are still syntactically valid).@gmloop/formatis Formatter-only (layout/canonical rendering) and does not own refactor transactions. The formatter never repairs invalid syntax and only formats valid AST.@gmloop/cliis the composition root that invokes refactor workflows through therefactorcommand.
Responsibilities
- Query parser span data and semantic bindings to map identifiers to source ranges.
- Plan edits that avoid scope capture or shadowing, and surface validation diagnostics.
- Offer composable helpers so CLI commands can trigger explicit refactor transactions.
- Re-run targeted analysis after edits to ensure symbol bindings remain stable.
- Support batch rename operations for refactoring related symbols atomically.
- Provide detailed impact analysis for dry-run scenarios.
- Validate hot reload compatibility to ensure refactored code can be patched live.
Features
- Naming-convention codemods treat unique constructor static members as cross-file rename targets, including dotted calls like
value.Sub()and bare calls likeReset()that occur inside constructors orwith (...)blocks. - Batch resource renames compose staged metadata rewrites and staged file moves, so later renames in the same run resolve the current folder/file path even for non-canonical GameMaker layouts that keep multiple object
.yyfiles in one directory.
Performance Regression Coverage
Current guardrails focus on the two hottest naming-convention paths that showed up in profiling:
- Selected-path filtering now compiles allow/deny lists once per codemod run instead of re-resolving them for every candidate.
WorkspaceEditapplication now assembles rewritten file content in a single pass, avoiding one full-string allocation per text edit.- Top-level naming-convention batch planning now reuses the first batch validation when the rename set is unchanged, avoiding a second full pass through
validateRenameRequest. - CLI local-variable naming scans now build each file's local-reference index once and reuse it for every declaration in that file.
- CLI semantic bridge lookups for script-backed callable declarations now use a resource-path index instead of rescanning every script entry for each lookup.
- Naming-convention planning now skips macro-expansion dependency scans for batches that only touch top-level/resource symbols, instead of parsing macro sources on every run whether local renames are present or not.
- Resource rename metadata planning now indexes inbound metadata references once per semantic bridge and reuses parsed
.yy/.yypdocuments across the batch instead of rescanning and reparsing them for every rename. WorkspaceEditnow caches grouped text edits per revision and tracks telemetry counters incrementally, avoiding extra full-edit scans in large codemod batches.- The CLI refactor command now uses the semantic workspace's default GML project-index concurrency instead of forcing a serial build, so large codemod runs do not bottleneck on one-file-at-a-time indexing.
- Globalvar and loop-length codemod executions now reuse source text captured during planning when applying a workspace edit, eliminating redundant per-file reads in dry-run and write modes.
- Semantic query caches now use least-recently-used eviction so hot symbol/file lookups survive cache pressure during large codemod batches.
The refactor workspace keeps naming-convention codemod stress tests in the regular TypeScript test suite:
src/refactor/test/naming-convention-performance.test.tsexercises high-volume local rename planning and edit application.src/cli/test/refactor-codemod-performance.test.tsexercises the indexed CLI bridge path for large top-level rename batches.src/cli/test/refactor-codemod-command-performance.test.tsexercises end-to-endrefactor codemod --writeexecution on a larger synthetic GameMaker project so CLI indexing, planning, and write-back stay bounded.src/cli/test/refactor-naming-target-discovery-performance.test.tsexercises naming-target discovery on mixed declaration/reference workloads so reference-only files do not rebuild local-reference indexes unnecessarily.src/cli/test/refactor-local-naming-performance.test.tsexercises disk-backed local-variable codemods so CI catches regressions in source-text loading, local-occurrence indexing, and member-access filtering on real files.src/cli/test/refactor-script-resource-naming-performance.test.tsexercises script-backed function naming on large resource sets so repeated script-resource scans stay bounded.src/cli/test/refactor-metadata-resource-naming-performance.test.tsexercises metadata-backed script resource renames on disk so repeated manifest/resource metadata parsing stays bounded.
Use pnpm run test:performance to execute only the performance suite locally.
Basic Usage
The RefactorEngine is initialized with a parser, a formatter, and a semantic analyzer interface. It orchestrates single-file and project-wide transactions (such as codemods and renames) using the WorkspaceEdit transaction container.
import { RefactorEngine } from "@gmloop/refactor";
const engine = new RefactorEngine({ parser, semantic, formatter });
// Plan and apply a rename transaction
const result = await engine.executeRename({
symbolId: "gml/script/scr_old_name",
newName: "scr_new_name",
readFile: async (path) => fs.promises.readFile(path, "utf8"),
writeFile: async (path, content) => fs.promises.writeFile(path, content)
});
For complete, compile-safe examples of how to query, validate, and execute complex refactoring scenarios (including batch renames, validation pre-flight checks, structural safety analyses, and hot reload compatibility checks), refer to the test suites in test/.
Naming Convention Enforcement (Policy Config)
Naming policy lives under refactor.codemods.namingConvention inside the unified
project-root gmloop.json. The namingConvention codemod reads that policy,
plans top-level renames through the batch rename engine, merges those edits with
local single-file renames into one workspace edit, and runs hot-reload
validation before apply.
Unsafe top-level renames are skipped with warnings so one conflicting symbol
does not abort the rest of the codemod run. Batch-planned metadata rewrites are
coalesced per metadata file so sequential resource renames compose into a
single .yy/.yyp update instead of conflicting duplicate rewrites, and write
mode avoids stale rename offsets by applying the merged workspace atomically.
Case-style rewrites preserve allowed leading and trailing underscore affixes, so
lower_snake policy does not silently strip names like __input_error unless
the policy explicitly bans those affixes. Conflict checking and reserved keyword
validation are case-sensitive because GameMaker Language is case-sensitive, so
renaming custom identifiers to target names that differ in casing from reserved
keywords or existing variables (e.g. custom macro LERP vs built-in lerp,
or same-scope myVar vs MY_VAR) is fully permitted and does not trigger conflicts.
Object-event assignment-backed fields
that the semantic index only reports as unresolved references are still treated
as instance-variable naming targets when they are introduced through instance
assignments, so project-wide variable policy can rename identifiers like
charMat to char_mat. Script resource names stay coupled to a same-name
top-level callable only when that file defines exactly one top-level callable;
multi-function script files expose the resource and each callable as
independent rename targets so policies can rename DemoLibrary and
function DemoLibrary() differently when needed, with resource renames
limited to metadata/path edits while callable renames own the text
occurrences inside .gml files.
Constructor renames also update parent-constructor clauses such as
function Child() : BaseType() constructor {}, and local naming rewrites skip
identifiers that referenced #macro expansions read from the caller scope so
the refactor output remains valid after GameMaker preprocesses macro bodies.
Cross-file enum and macro renames also collect unresolved top-level consumer
references from project file records so naming-convention runs keep
CM_RAY.MASK-style uses aligned with renamed declarations.
Within multi-callable script resources, each callable now keeps its own
declaration category: constructor or struct policies only affect the matching
declarations, and plain functions stay untouched unless the policy explicitly
configures the function category.
Contract
gmloop.jsonis the project config file.refactor.codemods.namingConventionis user-authored project config.refactor.codemods.namingConventionenables the codemod.rule exists => enabledis the contract.- There is no
enabledproperty on naming rules. If a rule is present for a category, that category is enabled. If a category is set tofalse, it is disabled even if a parent has a rule.
Project Config Shape
{
"printWidth": 95,
"lintRules": {
"gml/no-globalvar": "error"
},
"refactor": {
"codemods": {
"namingConvention": {
"rules": {
"resource": {
"caseStyle": "lower"
},
"roomResourceName": {
"prefix": "rm_"
},
"variable": {
"caseStyle": "camel"
},
"globalVariable": {
"prefix": "g_",
"caseStyle": "lower_snake"
},
"loopIndexVariable": false,
"callable": {
"caseStyle": "camel"
},
"macro": {
"caseStyle": "upper_snake"
}
},
"exclusivePrefixes": {
"rm_": "roomResourceName"
}
},
"scientificNotation": {}
}
}
}
CLI Usage
# Preview configured codemods and effective config
pnpm run cli -- refactor codemod --list
# Dry-run configured codemods for the whole project
pnpm run cli -- refactor codemod
# Apply only namingConvention to a subset of paths
pnpm run cli -- refactor codemod scripts/player --only namingConvention --write
Selected-path namingConvention runs now resolve naming targets with one
filtered semantic query for the whole file set instead of rescanning the full
project index once per file. The refactor test suite includes a tracked
stress test for this path, so the existing pnpm run test:refactor and
pnpm run test:report jobs catch regressions in both behavior and runtime.
Naming-target discovery now also preserves the semantic provider method context
(this) when invoking listNamingConventionTargets, so bridge-backed
project-root resolution keeps working during batched resource rename queries.
The CLI semantic bridge also keeps indexed name and symbol-id lookup tables for
rename validation, occurrence gathering, and scope checks, preventing large
codemod runs from repeatedly scanning every identifier collection for every
top-level rename candidate.
Unresolved project-file references are also indexed once per bridge session and reused during rename occurrence gathering instead of being rescanned across the full file map for every symbol. Batch rename planning now keeps the refactor semantic query cache warm while metadata overlays are staged, so later renames in the same codemod run can reuse symbol existence and occurrence lookups. When a resource rename still needs disk-backed fallback occurrence discovery, the CLI semantic bridge now builds one cached identifier-occurrence index per GML file and reuses it across the whole codemod session instead of reparsing or rescanning every file for every renamed resource.
Naming-convention edits also normalize semantic occurrence spans to exclusive
end indexes before generating workspace edits, and local-variable rename
targets explicitly exclude property/member access tokens (for example
enum_name.Member) so codemods do not corrupt valid member accesses when a
local identifier happens to share the same spelling.
Policy Shape
type NamingCaseStyle =
| "lower"
| "upper"
| "camel"
| "lower_snake"
| "upper_snake"
| "pascal";
type NamingCategory =
| "resource"
| "scriptResourceName"
| "objectResourceName"
| "roomResourceName"
| "spriteResourceName"
| "audioResourceName"
| "timelineResourceName"
| "shaderResourceName"
| "fontResourceName"
| "pathResourceName"
| "sequenceResourceName"
| "tilesetResourceName"
| "variable"
| "localVariable"
| "globalVariable"
| "instanceVariable"
| "staticVariable"
| "argument"
| "catchArgument"
| "loopIndexVariable"
| "callable"
| "function"
| "constructorFunction"
| "typeName"
| "structDeclaration"
| "enum"
| "member"
| "enumMember"
| "macro";
type NamingRuleConfig = {
caseStyle?: NamingCaseStyle;
prefix?: string;
suffix?: string;
minChars?: number;
maxChars?: number;
bannedPrefixes?: string[];
bannedSuffixes?: string[];
};
type NamingConventionPolicy = {
rules: Partial<Record<NamingCategory, NamingRuleConfig | false>>;
exclusivePrefixes?: Record<string, NamingCategory>;
exclusiveSuffixes?: Record<string, NamingCategory>;
};
type ResolvedNamingRule = {
prefix: string;
suffix: string;
caseStyle: NamingCaseStyle;
minChars: number | null;
maxChars: number | null;
bannedPrefixes: readonly string[];
bannedSuffixes: readonly string[];
};
type ResolvedNamingConventionRules = Partial<
Record<NamingCategory, ResolvedNamingRule>
>;
Built-In Category Hierarchy
The parent structure is built into the engine and does not need to be declared in user config:
const NAMING_CATEGORY_PARENTS: Record<NamingCategory, NamingCategory | null> = {
resource: null,
scriptResourceName: "resource",
objectResourceName: "resource",
roomResourceName: "resource",
spriteResourceName: "resource",
audioResourceName: "resource",
timelineResourceName: "resource",
shaderResourceName: "resource",
fontResourceName: "resource",
pathResourceName: "resource",
sequenceResourceName: "resource",
tilesetResourceName: "resource",
variable: null,
localVariable: "variable",
globalVariable: "variable",
instanceVariable: "variable",
staticVariable: "variable",
argument: "variable",
catchArgument: "variable",
loopIndexVariable: "localVariable",
callable: null,
function: "callable",
typeName: null,
structDeclaration: "typeName",
constructorFunction: "structDeclaration",
enum: "typeName",
member: null,
enumMember: "member",
macro: null
};
Enforcement Model
- Identify each symbol's category key from semantic data (for example
roomResourceName,function,localVariable). - Resolve each category using the built-in parent map (root -> leaf merge).
- Apply only explicitly provided properties as overrides (
caseStyle,prefix,suffix,minChars,maxChars,bannedPrefixes,bannedSuffixes). - If a category is set to
false, disable that category even if a parent has a rule. - If no rule exists for a category after inheritance, that category is not enforced.
- Validate in this order: banned affixes -> exclusive affixes -> required prefix/suffix -> length (
minChars/maxChars) -> case style (on the core name after removing required prefix/suffix). - Emit diagnostics for violations and provide refactor-driven rename plans to fix them.
- Run existing rename conflict checks before applying any generated rename transaction.
Notes
- Current runtime target coverage includes resource names, script/constructor/struct declarations, enums, enum members, macros, globals, instance variables, locals, static locals, loop indices, arguments, and catch arguments.
- Naming-convention planning expands selected
.gmlpaths to their owning resource.yyfiles, so object event rewrites also execute the matching object resource rename transaction instead of leaving code and metadata out of sync. - Implicit instance-variable coverage follows unresolved object-event assignments across related object event files, including inherited child-object reads and dotted object-property reads, while excluding known enum-owner member accesses such as
CM.Rso enum members are not folded into instance-variable renames. staticVariableandloopIndexVariableare syntax-refined local-variable categories. The refactor engine only exposes concrete categories that it can currently rename with complete occurrence coverage from the semantic bridge.- Prefix/suffix matching is strict and case-sensitive.
- Parent/category relationships are not stored in
ResolvedNamingRule; they are only used during rule resolution. lower_snakeandupper_snakeare both supported to enforce snake-case in either casing.exclusivePrefixesandexclusiveSuffixesare global reservations that apply even when a category has no required prefix/suffix.- If exclusive affixes overlap, use longest-match precedence to avoid ambiguous ownership.
- Resource naming-prefix enforcement replaces detectable legacy short prefixes when possible instead of duplicating them (for example
oSpider->obj_spider,sSpiderHead->spr_spider_head). minCharsandmaxCharsare checked against the core name after removing required prefix/suffix.- Cache resolved rules by category key so validation and rename previews stay fast.
- This policy remains centralized so IDE/CLI integrations enforce the same naming behavior.
Refactoring Features
Rename Operations
Allows planning and executing rename refactorings for both single symbols and batches of related symbols atomically, ensuring all files and project metadata (.yy, .yyp) are updated in a single transaction.
Impact Analysis
Analyzes the potential impact of a rename before committing to it. It returns summaries of affected files, occurrence kinds (definitions vs. references), dependent symbols, and whether hot reload is required.
Hot Reload Safety Check & Validation
Checks if a rename is safe for live hot reloading (e.g., scripts and instance variables vs. macros and keywords) and validates that a planned set of workspace edits will not break hot reload constraints.
Occurrence & Scope Validation
Provides utilities to classify, filter, and group occurrences, and to perform efficient batch scope-shadowing validation across scopes.
Performance Optimization
Semantic Query Cache
The refactor engine supports session-scoped caching of semantic analyzer queries to optimize batch operations and impact analysis (e.g., mapping symbol occurrences, dependents, file symbols, and existence). The SemanticQueryCache class provides LRU/FIFO eviction policy with a configurable max capacity and TTL.
Rename Validation Cache
A specialized cache for rename validation results. In interactive rename sessions (such as an IDE rename dialog), this caches structural, scope, and cross-file validation results to provide instant feedback as users type.
Directory layout
src/– core refactoring primitives and orchestrators.test/– Node tests that validate refactor strategies against fixture projects.
API Reference
RefactorEngine
Main class for coordinating refactoring operations.
Constructor:
new RefactorEngine({ parser, semantic, formatter });
Methods:
Rename Operations
async validateRenameRequest(request, options)- Validate a single rename request without creating edits (returns validation results instead of throwing)async validateBatchRenameRequest(renames, options)- Validate multiple rename requests before planning, detecting conflicts between renamesasync planRename(request)- Plan a single symbol renameasync planBatchRename(renames)- Plan multiple renames atomicallyasync executeRename(request)- Execute a rename with optional hot reloadasync executeBatchRename(request)- Execute multiple renames atomically
Analysis & Validation
async analyzeRenameImpact(request)- Analyze impact without applying changesasync validateRename(workspace)- Validate a workspace editasync validateHotReloadCompatibility(workspace, options)- Check hot reload compatibilityasync checkHotReloadSafety(request)- Check if a rename is safe for hot reloadasync verifyPostEditIntegrity(request)- Verify semantic integrity after applying edits
Workspace Operations
async applyWorkspaceEdit(workspace, options)- Apply edits to files (includeResultContent: falseavoids retaining full post-edit text in memory during write flows)async prepareRenamePlan(request, options)- Prepare a comprehensive rename plan with validationasync prepareBatchRenamePlan(renames, options)- Prepare a comprehensive batch rename plan with validation, optional impact analysis (includeImpactAnalyses), and optional hot reload metadata
Hot Reload Integration
async prepareHotReloadUpdates(workspace)- Prepare hot reload update metadataasync generateTranspilerPatches(hotReloadUpdates, readFile)- Generate transpiled patchesasync computeHotReloadCascade(changedSymbolIds)- Compute transitive dependency closure for hot reloadasync computeRenameImpactGraph(symbolId)- Compute detailed dependency impact graph with critical path analysis
Symbol Queries
async findSymbolAtLocation(filePath, offset)- Find symbol at positionasync validateSymbolExists(symbolId)- Check if symbol existsasync gatherSymbolOccurrences(symbolName)- Get all occurrences of a symbolasync getFileSymbols(filePath)- Query symbols defined in a specific fileasync getSymbolDependents(symbolIds)- Query symbols that depend on given symbols
Conflict Detection
async detectRenameConflicts(request)- Detect conflicts for a proposed rename operation without throwing errors
Validation Functions
Standalone utilities for validating rename requests:
async validateRenameStructure(symbolId, newName, resolver)- Fast structural validation of rename parameters before planning- Validates parameter presence, identifier syntax, and optional symbol existence
- Returns array of error messages (empty if valid)
- Enables fail-fast pattern without expensive occurrence gathering
detectCircularRenames(renames)- Detect circular rename chains in batch operations- Returns first detected cycle as array of symbol IDs (empty if no cycles)
async batchValidateScopeConflicts(occurrences, newName, resolver)- Efficiently validate scope safety across multiple occurrences- Groups occurrences by scope to minimize redundant lookups
- Returns map of scope IDs to conflict information
- Essential for hot reload scenarios where many symbols need validation quickly
- Reduces validation overhead by checking each unique scope only once
async validateCrossFileConsistency(symbolId, newName, occurrences, fileProvider)- Validate cross-file semantic consistency for renames- Checks whether renaming would create ambiguous references across files
- Detects file-level symbol conflicts where new name already exists
- Warns about large rename operations (>20 occurrences per file)
- Essential for multi-file refactorings and ensuring import/export consistency
- Returns array of conflict entries with file paths and severity levels
Occurrence Analysis Functions
Standalone utilities for analyzing symbol occurrences:
classifyOccurrences(occurrences)- Classify occurrences into categories (definitions, references, by file, by kind)filterOccurrencesByKind(occurrences, kinds)- Filter occurrences by kind (e.g., ["definition"], ["reference"])groupOccurrencesByFile(occurrences)- Group occurrences by file pathfindOccurrencesInFile(occurrences, filePath)- Find occurrences within a specific filecountAffectedFiles(occurrences)- Count unique files affected by occurrences
Rename Preview Functions
Utilities for generating human-readable previews and reports of rename operations:
generateRenamePreview(workspace, oldName, newName)- Generate a preview of changes that will be made by a workspace editformatRenamePlanReport(plan)- Format a rename plan summary as a multi-line text reportformatBatchRenamePlanReport(plan)- Format a batch rename plan summary as a multi-line text reportformatOccurrencePreview(occurrences, oldName, newName)- Format occurrence locations as a diff-style preview
These functions are essential for:
- IDE integrations that need to show diff-like previews before applying renames
- CLI tools that want to present detailed impact reports to users
- Automated refactoring pipelines that need to log changes before applying them
- Debugging refactoring operations by visualizing what will change
Hot Reload Functions
Standalone utilities for hot reload coordination and analysis:
computeHotReloadCascade(changedSymbolIds, semantic)- Compute transitive dependency closure for hot reloadcheckHotReloadSafety(request, semantic)- Check if a rename is safe for hot reloadprepareHotReloadUpdates(workspace, semantic)- Prepare hot reload update metadata from workspace editgenerateTranspilerPatches(hotReloadUpdates, readFile, formatter)- Generate transpiled patches from hot reload updatescomputeRenameImpactGraph(symbolId, semantic)- Compute detailed dependency impact graph with critical path analysis
The computeRenameImpactGraph function is particularly useful for:
- Visualizing the full scope of a rename's impact on the codebase
- Understanding dependency relationships and reload propagation
- Estimating hot reload timing and identifying performance bottlenecks
- Planning complex refactorings that affect multiple interconnected symbols
- Building interactive dependency visualization tools in IDEs
WorkspaceEdit
Container for text edits across multiple files.
Methods:
addEdit(path, start, end, newText)- Add a text editgroupByFile()- Group edits by file path (sorted for safe application)
SemanticQueryCache
Caching layer for semantic analyzer queries during refactoring operations.
Constructor:
new SemanticQueryCache(semantic, config);
Configuration:
maxSize- Maximum entries per cache type (default: 100). A value of0keeps the cache enabled but gives it zero capacity, so results are returned without being retained.ttlMs- Time-to-live in milliseconds (default: 60000)enabled- Enable/disable caching (default: true)
Methods:
async getSymbolOccurrences(symbolName)- Get cached symbol occurrencesasync getFileSymbols(filePath)- Get cached file symbolsasync getDependents(symbolIds)- Get cached dependent symbolsasync hasSymbol(symbolId)- Check cached symbol existenceinvalidateAll()- Clear all cached entriesinvalidateFile(filePath)- Clear entries for specific filegetStats()- Get cache performance statisticsresetStats()- Reset performance counters
Statistics:
hits- Number of cache hitsmisses- Number of cache missesevictions- Number of entries evicted due to size limitssize- Current total cache size across all types
RenameValidationCache
Caching layer for rename validation results during interactive refactoring.
Constructor:
new RenameValidationCache(config);
Configuration:
maxSize- Maximum cached validation results (default: 50)ttlMs- Time-to-live in milliseconds (default: 30000)enabled- Enable/disable caching (default: true)
Methods:
async getOrCompute(symbolId, newName, compute)- Get cached validation or compute new resultinvalidate(symbolId, newName)- Invalidate specific symbol-name pairinvalidateSymbol(symbolId)- Invalidate all cached validations for a symbolinvalidateAll()- Clear all cached validation resultsgetStats()- Get cache performance statisticsresetStats()- Reset performance counters
Statistics:
hits- Number of cache hitsmisses- Number of cache missesevictions- Number of entries evicted due to size limitssize- Current cache size
Status
The refactor engine now includes comprehensive rename planning, batch operations, impact analysis, hot reload validation, occurrence analysis utilities, rename preview and reporting utilities, advanced dependency cascade computation, detailed rename impact graph visualization, semantic query caching, and rename validation caching for performance optimization. It integrates with the semantic analyzer to provide safe, scope-aware refactoring operations with full transitive dependency tracking for hot reload scenarios. The impact graph computation provides critical path analysis and timing estimates, enabling IDE integrations and CLI tools to present detailed, human-readable reports of planned changes and their hot reload implications before applying them. The query cache layer optimizes repeated semantic queries during batch operations, while the validation cache layer speeds up interactive rename workflows by caching validation results as users type new names, significantly improving performance for complex refactoring workflows and providing instant feedback in IDE rename dialogs.
TODO
- FEAT: For renaming script files, we should allow for specifying a different naming convention for ones that contain a single struct definition. For example, if we have "LinkedHashMap.gml" that defines a single struct called
LinkedHashMap, we should allow for the file to be renamed to match the struct name (e.g., "LinkedHashMap.gml") without being flagged for renaming, even if it doesn't follow the standard script naming convention (e.g.,scr_prefix). This would allow for more natural naming of struct files while still enforcing naming conventions for regular scripts. - For the renaming fix, we should support an allow/deny list of prefixes, suffixes, and names that are exempt from renaming. For example, if a project's
gmploop.jsonspecifies that sprites must use thespr_prefix, the rename configuration should also allow exceptions such as sprites with the tex_ prefix so they are not flagged for renaming. - Alternatively, instead of requiring a specific prefix or suffix, we could support a denylist of disallowed names, prefixes, or suffixes. So, resources would only be flagged for renaming if they match an entry in the denylist. For example, if a resource is named
__appleand the denylist includes the prefix__, it would be flagged for renaming, since it matches a disallowed naming pattern. In this mode, renaming would follow a default or separately defined naming rule (e.g., a standard prefix/suffix or pattern), applied only when a name violates the denylist. In this mode, a resource that matches the denylist would first check its inheritance tree and try to inherit a valid naming prefix from its parent chain. If no applicable prefix is found, it should attempt to remove the disallowed prefix, provided the result passes all safety checks. If that still fails, it should fall back to the default naming convention. - PERF: The
loopLengthHoistingcodemod seems to take a really long time to run on large projects. We should investigate ways to optimize its performance, possibly by caching results (if not already), improving the algorithm used for hoisting loop lengths, do pattern searches across the project instead of parsing files individually, etc. This could involve analyzing the current implementation to identify bottlenecks and exploring alternative approaches that reduce the number of passes over the code or minimize expensive operations. Ex. output from a large project run:[loopLengthHoisting] running... [loopLengthHoisting] Processing files... (0/379) [loopLengthHoisting] Processing files... (117/379) ... [loopLengthHoisting] Processing files... (312/379) [loopLengthHoisting] Processing files... (379/379) [loopLengthHoisting] no changes (took 55.52s)