Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subcommand syntax highlighting #940

Open
JamesTeague opened this issue Nov 17, 2023 · 8 comments
Open

Subcommand syntax highlighting #940

JamesTeague opened this issue Nov 17, 2023 · 8 comments

Comments

@JamesTeague
Copy link

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.

@DimiM99
Copy link

DimiM99 commented Nov 22, 2023

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
Copy link
Member

danielshahaf commented Nov 22, 2023 via email

@danielshahaf
Copy link
Member

danielshahaf commented Nov 22, 2023 via email

@JamesTeague
Copy link
Author

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

@danielshahaf
Copy link
Member

danielshahaf commented Dec 12, 2023 via email

@mattmc3
Copy link

mattmc3 commented Mar 13, 2024

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

@mattmc3
Copy link

mattmc3 commented Mar 13, 2024

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

@danielshahaf
Copy link
Member

danielshahaf commented Mar 14, 2024 via email

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

No branches or pull requests

4 participants