Subcommand syntax highlighting
JamesTeague opened this issue · 8 comments
@danielshahaf the behavior I would expect that the first argument should be highlighted. Preferably, it would be highlighted if it was a valid argument. In the case of docker build
docker gets highlighted as a command and build
would too but something like docker buffer
would not highlight the argument buffer
. I understand that the add enhancement of being a valid sub-command may not be in the scope of this project. Coming from zsh-fast-syntax-highlighting and using zsh auto-completions, I thought this may be something that could be feasible.
I have a similar issue where only first word in command is highlighted, the rest is not.
@DimiM99 - What you're seeing is the default behavior. From the docs:
main - the base highlighter, and the only one active by default.
If you want additional highlighters, add them to the ZSH_HIGHLIGHT_HIGHLIGHTERS
array:
typeset -a ZSH_HIGHLIGHT_HIGHLIGHTERS=(
main
brackets
regexp
pattern
line
cursor
root
)
Or, to add other 'main' highlighters, you need to do things like modify the ZSH_HIGHLIGHT_STYLES
array:
# Highlight command flags (-f|--flag).
typeset -A ZSH_HIGHLIGHT_STYLES
ZSH_HIGHLIGHT_STYLES[single-hyphen-option]=fg=cyan
ZSH_HIGHLIGHT_STYLES[double-hyphen-option]=fg=blue
Subcommands are kinda tricky. How do you know whether something is a subcommand or simply a positional argument without coding in all the subcommands or calling out to the command? That's how fast-syntax-highlighting does it, but it's a maintenance burden. If you are okay with a simpler (naive) solution, you could simply assume the second word is always a subcommand if that word starts with [[:alnum:]]
and contains no punctuation except dash/underscore. Perhaps a project maintainer can weigh-in on whether this is adequate for inclusion in this project and I'll submit a PR. In the meantime, you can just source this in your own .zshrc config:
# Highlight subcommands (the second word) in yellow.
ZSH_HIGHLIGHT_STYLES[subcommand]='fg=yellow'
# Whenever the buffer is modified, evaluate whether there's a subcommand.
_zsh_highlight_highlighter_subcommand_predicate() {
_zsh_highlight_buffer_modified
}
_zsh_highlight_highlighter_subcommand_paint() {
# Short-circuit check to ensure there are multiple words.
local -a words=(${=BUFFER})
(( $#words > 1 )) || return
# Find the second word
local startpos=0 pos=0 state=start char=
for char in ${(s..)BUFFER}; do
case $state in
start)
if [[ "$char" == [[:alnum:]] ]]; then
state=cmd
elif [[ "$char" == '#' ]]; then
state=comment
break
fi
;;
cmd)
if [[ "$char" == [[:space:]] ]]; then
state=whitespace
fi
;;
whitespace)
if [[ "$char" == [[:alnum:]] ]]; then
startpos=$pos
state=subcmd
elif [[ "$char" != [[:space:]] ]]; then
# If the next word begins with a non-space, non-alnum char
# (eg: punctuation), then it's likely a --flag, $var, ~/path,
# or something else - not a proper subcommand
state=none
break
fi
;;
subcmd)
if [[ "$char" == [[:space:]] ]]; then
break
elif [[ "$char" != [[:alnum:]] ]] && [[ "$char" != (-|_) ]]; then
state=none
break
fi
;;
esac
(( ++pos ))
done
if [[ "$state" == subcmd ]] && (( $startpos < $pos )); then
_zsh_highlight_add_highlight $startpos $pos 'subcommand'
fi
}
# Add your custom subcommand highlighter
ZSH_HIGHLIGHT_HIGHLIGHTERS+=(subcommand)
Here's a visual: