fix(import): fold sources differing only in case into one plugin - #35
Closed
zuqini wants to merge 1 commit into
Closed
fix(import): fold sources differing only in case into one plugin#35zuqini wants to merge 1 commit into
zuqini wants to merge 1 commit into
Conversation
A plugin installs into a directory named after it, so two specs resolving to the same name compete for it. vim.pack.add finds out only when git refuses the second clone with "destination path already exists and is not an empty directory" -- a message naming neither spec, which aborts setup() for the whole config. Same repository, different casing (zuqini/ZSnip.nvim vs zuqini/zsnip.nvim) is one plugin: git hosts match owner and repo case-insensitively. Those now fold onto the casing imported first and their specs merge. The fold lives in normalize_source, the funnel both import_one_spec and register_dependencies go through, so the dependency graph is keyed by the surviving source too -- folding at the registration site alone left edges naming a source no registry entry had, and every consumer guards on the entry existing, so a lazy parent silently never loaded its dependency. Genuinely different plugins claiming one name are reported against both sources, keeping the one imported first. Names differing only in case are warned about but both kept: they are two directories on a case-sensitive filesystem, and dropping one there would remove a plugin that installs fine. Collision resolution runs after prune_disabled so a disabled spec cannot win a directory it was never going to occupy, and drops through the same worklist as prune_disabled so a skipped plugin takes its dep-only dependencies with it rather than leaving them installed and orphaned.
zuqini
force-pushed
the
fix/name-collisions
branch
from
August 2, 2026 05:44
97e8564 to
41c0501
Compare
Owner
Author
|
Closing — not worth the complexity.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A plugin installs into a directory named after it, so two specs resolving to the same name compete for it.
vim.pack.addfinds out only when git refuses the second clone withdestination path '.../ZSnip.nvim' already exists and is not an empty directory— a message that names neither spec and abortssetup()for the whole config.Hit while migrating a config: one spec file declared
zuqini/ZSnip.nvim, another listedzuqini/zsnip.nvimin itsdependencies.Same repository, different casing. Git hosts match owner and repo case-insensitively, so these are one plugin. They now fold onto the casing imported first and their specs merge, exactly as if both had been written alike. Folding happens in
normalize_source— the single funnel bothimport_one_specandregister_dependenciesgo through, sodependency_graphis keyed by the surviving source too. Local sources are deliberately not folded: on a case-sensitive filesystem two paths differing in case are two directories.Genuinely different plugins claiming one name (
alice/shared.nvimvsbob/shared.nvim). Reported against both sources, keeping the one imported first:Names differing only in case (
alice/shared.nvimvsbob/Shared.nvim) are warned about but both kept — they are two directories on ext4, and dropping one there would remove a plugin that installs perfectly well. The warning is what explains thevim.packerror on macOS or Windows.Collision resolution runs after
prune_disabled, so a disabled spec cannot win a directory it was never going to occupy, and walks the registry in import order so the winner does not vary between starts. Drops go through the same worklist asprune_disabled, so a skipped plugin takes its dep-only dependencies with it instead of leaving them installed and orphaned.Prior art: lazy.nvim keys its plugin registry by derived name, which is what makes the same plugin referenced two ways collapse into one. zpack keys by source, so the fold happens at import instead.
Tests
14 cases in
tests/name_collision_test.luacovering folding, spec merging across casings, dependency-graph keying, lazy-parent dependency loading, first-casing-wins, local dirs staying separate, the collision report, case-variants coexisting,namebreaking the tie, disabled specs not winning, and orphan cleanup. Full suite 497 passing; luacheck and lua-language-server clean.Docs:
doc/zpack.txt(zpack-gotcha-name-collision, cross-referenced fromzpack-Spec.name),docs/tips.md,docs/spec.md.