zsh-users/zsh-syntax-highlighting

Subcommand syntax highlighting

JamesTeague opened this issue · 8 comments

Here is what I have
Screenshot 2023-11-17 at 16 11 00

Here is what I am looking to do.
2023-11-17_16-09

The argument or sub-command (honestly, not sure the proper term) should be highlighted a color. I am not sure which tweak to use to get that result or if this should be an enhancement request.

I have a similar issue where only first word in command is highlighted, the rest is not.

Screenshot 2023-11-22 at 14 54 05

context:
oh-my-zsh
iTerm2
p10k
JetBrainsMonoNL NF

@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:

Screenshot 2024-03-13 at 2 09 01 PM