mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Update deduplication logic and documentation for suffix handling
- Removed the `luau` dependency from `rokit.toml`. - Updated deduplication logic to append `~2`, `~3`, etc. instead of `~1`, `~2` for naming collisions in various files, ensuring consistent suffix management. - Adjusted comments and documentation across multiple files to reflect the new suffix handling rules. - Added new test cases to validate the updated deduplication behavior, ensuring accurate naming for instances with collisions. These changes enhance the clarity and functionality of the deduplication process, improving overall consistency in naming conventions.
This commit is contained in:
parent
a59d9d497a
commit
d836ebddc8
35 changed files with 349 additions and 281 deletions
|
|
@ -92,10 +92,10 @@ pub fn deduplicate_name_with_ext(
|
|||
taken_names: &HashSet<String>,
|
||||
) -> (String, String)
|
||||
// Returns: (deduped_slug, full_fs_name)
|
||||
// "Foo" + ".server.luau" with collision → ("Foo~1", "Foo~1.server.luau")
|
||||
// "Foo" + ".server.luau" with collision → ("Foo~2", "Foo~2.server.luau")
|
||||
```
|
||||
|
||||
**Key invariant:** Suffix goes on the stem BEFORE the compound extension: `Foo~1.server.luau`, NOT `Foo.server~1.luau`.
|
||||
**Key invariant:** Suffix goes on the stem BEFORE the compound extension: `Foo~2.server.luau`, NOT `Foo.server~2.luau`.
|
||||
|
||||
### Dedup Key Semantics
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ When the filesystem name differs from the instance name (due to slugification or
|
|||
```
|
||||
Instance "Hey/Bro" → Hey_Bro.luau + meta: {"name": "Hey/Bro"}
|
||||
Instance "Folder" → Folder/ (no meta needed, name == slug)
|
||||
Instance "Folder" → Folder~1/ + meta: {"name": "Folder"}
|
||||
Instance "Folder" → Folder~2/ + meta: {"name": "Folder"}
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -155,11 +155,11 @@ pub enum DedupCleanupAction {
|
|||
|
||||
### Three Cleanup Rules
|
||||
|
||||
1. **Gap-tolerant deletion:** Deleting `~1` from `{Foo, Foo~1, Foo~2}` leaves `{Foo, Foo~2}`. No renumbering. Gaps are tolerated.
|
||||
1. **Gap-tolerant deletion:** Deleting `~2` from `{Foo, Foo~2, Foo~3}` leaves `{Foo, Foo~3}`. No renumbering. Gaps are tolerated.
|
||||
|
||||
2. **Base-name promotion:** Deleting `Foo` (base) from `{Foo, Foo~1, Foo~2}` promotes `Foo~1` to `Foo`. `Foo~2` stays. The lowest-numbered suffix becomes the new base.
|
||||
2. **Base-name promotion:** Deleting `Foo` (base) from `{Foo, Foo~2, Foo~3}` promotes `Foo~2` to `Foo`. `Foo~3` stays. The lowest-numbered suffix becomes the new base.
|
||||
|
||||
3. **Group-to-1 cleanup:** Reducing the group to 1 member removes the suffix entirely. Deleting from `{Foo, Foo~1}` leaves one instance → renamed to clean `Foo`.
|
||||
3. **Group-to-1 cleanup:** Reducing the group to 1 member removes the suffix entirely. Deleting from `{Foo, Foo~2}` leaves one instance → renamed to clean `Foo`.
|
||||
|
||||
### compute_cleanup_action
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ Ref paths use **full filesystem names** including extensions and dedup suffixes:
|
|||
Folder "Foo" → segment "Foo"
|
||||
ModuleScript "Foo" → segment "Foo.luau"
|
||||
Script "Foo" (server) → segment "Foo.server.luau"
|
||||
Dedup'd ModuleScript → segment "Foo~1.luau"
|
||||
Dedup'd ModuleScript → segment "Foo~2.luau"
|
||||
Init-style Script → segment "Foo" (directory)
|
||||
```
|
||||
|
||||
|
|
@ -395,7 +395,7 @@ Dedup renames must suppress VFS events to prevent feedback loops:
|
|||
|
||||
### Ref Path Updates After Dedup Rename
|
||||
|
||||
After successful dedup rename (e.g., `Foo~1` → `Foo`):
|
||||
After successful dedup rename (e.g., `Foo~2` → `Foo`):
|
||||
1. Compute old and new ref path segments
|
||||
2. Call `update_ref_paths_after_rename(&old_prefix, &new_prefix)`
|
||||
3. This function uses `RefPathIndex` to find affected files
|
||||
|
|
@ -475,8 +475,8 @@ When adding instances, dedup is handled via `sibling_slugs: &mut HashSet<String>
|
|||
2. **CLI syncback parity:** two-way sync produces byte-identical filesystem output to CLI syncback
|
||||
3. **Determinism:** same input always produces same filesystem output (sorted child ordering, deterministic suffix assignment)
|
||||
4. **InstigatingSource consistency:** dedup'd paths reflected in `InstigatingSource::Path`
|
||||
5. **Compound extensions:** suffix before compound extension (`Foo~1.server.luau`, NOT `Foo.server~1.luau`)
|
||||
6. **Meta pairing:** `Foo~1.server.luau` pairs with `Foo~1.meta.json5` (strip script suffix, keep dedup suffix)
|
||||
5. **Compound extensions:** suffix before compound extension (`Foo~2.server.luau`, NOT `Foo.server~2.luau`)
|
||||
6. **Meta pairing:** `Foo~2.server.luau` pairs with `Foo~2.meta.json5` (strip script suffix, keep dedup suffix)
|
||||
7. **Dedup key = full FS name:** collisions checked using complete filesystem name (slug + extension), lowercased
|
||||
8. **Co-removed exclusion:** batch removal skips co-removed siblings when computing cleanup actions
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ Add metadata to file-based instances. Meta files use **camelCase keys** (no `$`
|
|||
|
||||
**Key differences from ProjectNode:** Meta uses `className`, `properties`, `attributes` (camelCase, no `$` prefix). Adjacent meta does NOT support `className`.
|
||||
|
||||
**`name` field (slugify support):** When an instance name contains forbidden filesystem characters (`: / ? * < > | " \`), syncback slugifies the filename (forbidden chars → `_`) and stores the real name in the `name` field. On forward sync, `name` overrides the filename-derived instance name. On collisions (e.g. `A/B` and `A_B` both slugify to `A_B`), `~1`, `~2` suffixes are appended and meta `name` fields distinguish them.
|
||||
**`name` field (slugify support):** When an instance name contains forbidden filesystem characters (`: / ? * < > | " \`), syncback slugifies the filename (forbidden chars → `_`) and stores the real name in the `name` field. On forward sync, `name` overrides the filename-derived instance name. On collisions (e.g. `A/B` and `A_B` both slugify to `A_B`), `~2`, `~3` suffixes are appended and meta `name` fields distinguish them.
|
||||
|
||||
**`Rojo_Ref_*` attributes:** Meta files may also contain `Rojo_Ref_PropertyName` attributes written by syncback/two-way sync for Ref property linking. These are managed automatically and should not be hand-edited.
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub struct InstanceContext {
|
|||
|
||||
Instance names with forbidden filesystem chars (`<>:"/\|?*~`) are handled by:
|
||||
1. `slugify_name()` -- replaces forbidden chars with `_` (pure, stateless)
|
||||
2. `deduplicate_name_with_ext()` -- dedup key = full filesystem name (slug + extension), case-insensitive; appends `~1`, `~2`, etc. on collision (stateful)
|
||||
2. `deduplicate_name_with_ext()` -- dedup key = full filesystem name (slug + extension), case-insensitive; appends `~2`, `~3`, etc. on collision (stateful)
|
||||
3. Metadata `name` field -- stores the real instance name in `.meta.json5`/`.model.json5` when the slug differs
|
||||
|
||||
### 3. Commands (`src/cli/`)
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@ Manages `~N` suffix cleanup after instance deletions during two-way sync.
|
|||
- `compute_cleanup_action(removed_stem, remaining_stems) -> DedupCleanupAction` - Determine what to do after a deletion
|
||||
|
||||
**Cleanup rules:**
|
||||
- **Gap-tolerant:** Deleting `~1` from `{Foo, Foo~1, Foo~2}` leaves `{Foo, Foo~2}`. No renumbering.
|
||||
- **Base-name promotion:** Deleting `Foo` (base) from `{Foo, Foo~1, Foo~2}` promotes `Foo~1` to `Foo`. `Foo~2` stays.
|
||||
- **Gap-tolerant:** Deleting `~2` from `{Foo, Foo~2, Foo~3}` leaves `{Foo, Foo~3}`. No renumbering.
|
||||
- **Base-name promotion:** Deleting `Foo` (base) from `{Foo, Foo~2, Foo~3}` promotes `Foo~2` to `Foo`. `Foo~3` stays.
|
||||
- **Group-to-1 cleanup:** If group shrinks to 1 member, remove its suffix entirely.
|
||||
|
||||
## Syncback Rules
|
||||
|
|
@ -285,12 +285,12 @@ snapshot.with_new_path(path, new_ref, old_ref)
|
|||
|
||||
**Filename generation** (`file_names.rs`):
|
||||
- `slugify_name()`: forbidden chars (`<>:"/\|?*~`) → `_`. Also strips leading/trailing spaces/dots, neutralizes dangerous suffixes (`.server`, `.meta`, etc.), handles Windows reserved names (`CON`, `PRN`, etc.)
|
||||
- `deduplicate_name_with_ext()`: Dedup key = full filesystem name (slug + extension), case-insensitive. `Folder "Foo"` (key `"foo"`) and `Script "Foo"` (key `"foo.server.luau"`) do NOT collide. Appends `~1`, `~2`, etc. on collision, with suffix inserted before compound extension: `Foo~1.server.luau`
|
||||
- `deduplicate_name_with_ext()`: Dedup key = full filesystem name (slug + extension), case-insensitive. `Folder "Foo"` (key `"foo"`) and `Script "Foo"` (key `"foo.server.luau"`) do NOT collide. Appends `~2`, `~3`, etc. on collision, with suffix inserted before compound extension: `Foo~2.server.luau`
|
||||
- `name_for_inst()` returns `(filename, needs_meta_name, dedup_key)`. `needs_meta_name` is true when the filesystem name differs from the real instance name.
|
||||
- `~` is in `SLUGIFY_CHARS` so natural tilde names (e.g., `"Foo~1"`) get slugified to `"Foo_1"`, avoiding confusion with dedup suffixes.
|
||||
- Example: `Script` named "My Script" → `My Script.server.luau`
|
||||
- Example: `Script` named "Hey/Bro" → `Hey_Bro.server.luau` + meta with `name: "Hey/Bro"`
|
||||
- Example: Two `Folder "Data"` → `Data/` + `Data~1/` (meta: `name: "Data"`)
|
||||
- Example: Two `Folder "Data"` → `Data/` + `Data~2/` (meta: `name: "Data"`)
|
||||
|
||||
### Property Filtering
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue