Aside: I suggest adding PS1="" to the input of zsh -fis for the Test/K01 patch.
What is the advantage? It doesn't seem to make any difference.
Sorry, the following is way too long for these two small patches but hopefully it will make it clear where I'm coming from and let you pinpoint what, if anything, you disagree with.
Here is my understanding of named directories minus the features not relevant for the current discussion.
Zsh maintains a table of named directories that maps directory names to directories. The table is accessible via the parameter nameddirs. The table supports two features:
- Filename abbreviation: When Zsh prints a filename F (e.g., /foo123/bar), it checks whether the table contains a name N (e.g., foo) whose directory D (e.g., /foo123) is a prefix of F. If yes, instead of printing $F (i.e., /foo123/bar), it prints ~$N/${F#$D} (i.e., ~foo/bar).
- Named directory expansion: When Zsh expands a filename F that starts with ~N, it checks whether the table contains a name N mapped to a directory D. If no name N is found in the table, Zsh checks whether there is a parameter P named N whose value is a directory D (i.e., any value that starts with "/"). If yes, it adds the pair (N, D) to the table and flags the parameter P with PM_NAMEDDIR. In both cases, ~N is replaced with D when F is expanded.
Once a parameter P named N is flagged with PM_NAMEDDIR, each time it is updated with a new value D, the pair (N, D) is added to the table of named directories if D starts with "/" and otherwise any entry whose name is N is removed from the table.
One can distinguish two kinds of named directories:
- Hash-based: When a named directory N is defined with the "hash" command (e.g. "hash -d foo=/foo123), then it only lives in the table of named directories; no parameter named N is needed and defining or updating one before or after the call to "hash" has no effect.
- Parameter-based: When a named directory is defined by first defining a parameter P named N and then expanding the filename ~N (e.g., "foo=/foo123; : ~foo"), then it's backed by the parameter P; any update of the value of the parameter P also updates the named directory N.
With a parameter-based named directory N, one could expect that ~N always expands to the same as $N but that isn't true if one calls "hash" after defining the named directory:
% zsh -fic 'foo=/foo1; : ~foo; hash -d foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo1 ~foo=/foo2
One could argue that the call to "hash" turned the parameter-based named directory into a hash-based one but that isn't the case either. Indeed, updating the parameter after calling "hash" still updates the named directory:
% zsh -fic 'foo=/foo1; : ~foo; hash -d foo=/foo2; foo=/foo3; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo3 ~foo=/foo3
To me this looks more like a bug than a feature. It could be addressed either by updating the value of the parameter or by removing the PM_NAMEDDIR flag from the parameter when "hash" is called. The former would ensure that $N and ~N remain in sync, the latter would turn the parameter-based named directory into a true hash-based one.
My assumption is that for a parameter-based named directory N, it is in principle expected that ~N and $N remain in sync. The example above shows that this can be violated. However, to the best of my knowledge, it is true as long as one never uses "hash" to modify parameter-based named directories.
When parameters are searched for the expansion of ~N (because there isn't yet any N in the table of named directories),
the search is restricted to scalar parameters. One could argue that the search should be expanded to named references that refer to a scalar parameter. This was actually the case before
workers/54475. However that broke the assumption above; even in the absence of calls to "hash", ~N and $N could get out of sync:
% zsh -fic 'var=/foo1; typeset -n foo=var; : ~foo; foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo2 ~foo=/foo1
Fixing this without extra infrastructure would be way too expensive; every update of a scalar parameter would have to scan the whole parameter table to check whether there is a named reference flagged with PM_NAMEDDIR that refers to it. The extra infrastructure looks way too overblown for such a small feature. To me, restricting parameter-based named directories to scalar parameters looks like the most pragmatic approach.
Directory values
When parameters are searched for the expansion of ~N, only parameters whose expansion starts with a "/" are considered:
% zsh -fic 'foo=no-slash; : ~foo; printf "~foo=%s\n" ~foo'
zsh:1: no such user or named directory: foo
When parameters flagged with PM_NAMEDDIR are updated with a value that doesn't start with a "/", the corresponding named directory is removed from the table instead of being updated with the new value:
% zsh -fic 'foo=/foo1; : ~foo; printf "~foo=%s\n" ~foo; foo=no-slash; printf "~foo=%s\n" ~foo'
~foo=/foo1
zsh:1: no such user or named directory: foo
If the "hash" command is used to define or update a named directory, then surprisingly all values are accepted:
% zsh -fic 'hash -d foo=no-slash; printf "~foo=%s\n" ~foo'
~foo=no-slash
It's unclear to me whether the fact that the "hash" command doesn't restrict values to ones that start with a "/" is a bug or a feature.
AUTO_NAME_DIRS option
The aim of the
AUTO_NAME_DIRS option is to avoid the need to expand ~N in order to create a parameter-based named directory N; one can simply define the parameter N.
A side-effect of the AUTO_NAME_DIRS option is that it makes it possible to turn a hash-based named directory N into a parameter-based one. Indeed, if a parameter named N is defined after the call to "hash", it will override the value specified by the "hash" command and any further updates of the parameter named N will be reflected in the named directory N:
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo2 ~foo=/foo2
The
documentation of the AUTO_NAME_DIRS option states that only parameters whose value is an absolute directory become named directories. However, the current implementation promotes all scalar parameters and all named references to named directories (i.e., flags them with PM_NAMEDDIR) whenever their value is initialized or updated.
As discussed above, parameter-based named directories backed by named references aren't supported by the current implementation because it can't ensure that ~N and $N remain in sync. An additional issue is that the code that promotes named references to named directories initializes the named directory with the name of the referred parameter instead of initializing it with the value of the referred parameter.
A better description of the patch
workers/54759 is that it prevents the promotion of named references to named directories. Given the current shortcomings and given the fact that named references can never be initialized with values that start with "/", the only noticeable effect of the patch is that initializing a named reference N after creating a hash-based named directory N, will no longer remove the named directory:
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset -n foo=var; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=() # Before patch
nameddirs=(foo /foo1) # After patch
A better description of the patch
workers/54760 is that it prevents the promotion to named directories of (scalar) parameters whose value doesn't start with a "/".
When AUTO_NAME_DIRS is disabled, declaring, initializing and/or updating a parameter never has any effect on a hash-based named directory:
% zsh -fic 'hash -d foo=/foo1; foo=/foo2; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)
When AUTO_NAME_DIRS is enabled, the same is still true for any non-scalar and non-nameref parameter:
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=(/foo2); : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset -i foo=1/2; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)
It is even true for scalar declarations with no initialization:
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset foo; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)
Given the above and given the fact that the
documentation of the AUTO_NAME_DIRS option states that only parameters whose value is an absolute directory become named directories, it seems reasonable to think that initializing or updating a scalar parameter with a value that is not a directory (i.e., that doesn't start with a "/") should leave any existing named directory unchanged:
% zsh-dev -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=foo2; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=() # Before patch
nameddirs=(foo /foo1) # After patch
and should not flag the parameter with PM_NAMEDDIR:
% zsh -fic 'setopt autonamedirs; foo=foo1; hash -d foo=/foo2; setopt +o autonamedirs; foo=foo3; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=() # Before patch
nameddirs=(foo /foo2) # After patch
In other words, only scalar parameters that effectively store absolute directory names are ever promoted to named directories.
Philippe