Skip to content

fix(shell): parse fish aliases and abbreviations - #154

Merged
versenilvis merged 5 commits into
versenilvis:mainfrom
ItzTas:fix/fish-alias-abbr-parsing
Aug 30, 2026
Merged

fix(shell): parse fish aliases and abbreviations#154
versenilvis merged 5 commits into
versenilvis:mainfrom
ItzTas:fix/fish-alias-abbr-parsing

Conversation

@ItzTas

@ItzTas ItzTas commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

closes #153

FishAdapter.ScanAliases sent config.fish through ScanPosixAliases, which reads bash/zsh syntax, so on a normal fish config you get almost nothing back and what you do get can be wrong. abbr wasn't read at all

alias clear 'clear -x'
abbr -a gl git log
if type -q lsd
    alias ls 'lsd --icon=always'
else
    alias ls 'ls --color=auto'
end
alias g='git'

before:  map[string]string{"'lsd --icon":"always'"}
after:   aliases map[clear:clear -x  g:git  ls:lsd --icon=always]
         abbrs   map[gl:git log]

The four problems from the issue, plus one that only showed up against a real config:

  • alias name value has no = to split on so those lines were dropped, and when the value carried its own = the line survived but split in the wrong place. Both forms parse now, with -s/--save and -- handled (1dd9d53)
  • blocks were counted with if/else/fi. fish closes everything with end, and that includes for/while/switch/begin/function, so counting only if lets an inner end pop the enclosing one and the fallback arm starts overwriting the preferred one. It's a stack now, first arm still wins, switch/case included (1dd9d53)
  • only config.fish was read. Now functions/*.fish, conf.d/*.fish and config.fish, in that precedence order, since an autoloaded function only loads while its name is still undefined. $__fish_config_dir is resolved once per process, same trick as ZDOTDIR (1dd9d53)
  • abbr parses in the forms people write: -a/--add, implicit add, --, --position, --set-cursor, -g. --regex and --function are skipped since neither the name nor the expansion is in the text, and a name the shell computes gets dropped instead of recorded as a literal. They reach the menu with their own source and icon, which fits the split from Add semantic, configurable icons for suggestion types #60 (6cec6ff)
  • the fifth one: following source needed set and for resolved too. My own config names its parts in a list and sources them from a loop, so without that the parser never reaches a single alias file (1dd9d53)
for file in $files
    set -l config_file "$__fish_config_dir/config/$file.fish"
    path is -f -- "$config_file"; and source "$config_file"
end

The walker was split out first (d8fa4a1) so the posix path and the fish one share the source following and cache stamping instead of duplicating both

Note

aliases and abbrs come out of one walk and share one cache entry. Two scans with different entry points would evict each other and re-read the whole tree on every keystroke

abbrs aren't wired into core.expand-alias: fish expands them itself on space, and a --set-cursor abbr would get its marker typed literally

integration/shell/fish_test.go covers the alias and abbr forms, arm preference with nested blocks, statement separators, alias --save function files, the autoloaded dirs, loop-sourced files, and cache invalidation when a file lands in conf.d/. Against my real config it finds 27/27 aliases and 39/39 plain abbrs; the other 50 are the --command-scoped ones built by that loop with (string split ...) names, which no static parser can resolve. just test, just lint and go vet ./... are clean

ItzTas and others added 5 commits August 29, 2026 15:20
Alias scanning hardcoded the POSIX grammar, so a shell with different
block and assignment syntax could not reuse the source-following and
cache-stamping logic around it.
ScanPosixAliases read config.fish with bash/zsh rules, so fish returned
almost nothing: `alias name value` has no equals sign to split on, and
`end` never closed a block that `fi` was counting. It also read only
config.fish, missing conf.d/, autoloaded functions/, and the files a
config reaches through `set`/`for` rather than a literal path. abbr was
not read at all.
Abbreviations are how fish users shorten a command, and unlike an alias
one expands in place, so listing the name next to what the shell will
put on the line matches what happens next. They carry their own source
and icon rather than passing as aliases.
@versenilvis
versenilvis merged commit c579523 into versenilvis:main Aug 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fish: alias parsing returns wrong entries, and abbr is not supported at all

2 participants