sainnhe / gruvbox-material

Gruvbox with Material Palette
MIT License
1.96k stars 166 forks source link

Strange Syntax Highlighting Inside Bash Functions #213

Closed StrangeRanger closed 1 month ago

StrangeRanger commented 2 months ago

I have done the following steps before reporting this issue:

Operating system/version

macOS Sonoma 14.6.1

Terminal emulator/version

Terminal.app (Version 2.14 (453))

$TERM environment variable

xterm-256color

Tmux version

No response

Feature matrix

==============================================================================
nvim: require("nvim.health").check()

Configuration ~
- OK no issues found

Runtime ~
- OK $VIMRUNTIME: /usr/local/Cellar/neovim/0.10.1/share/nvim/runtime

Performance ~
- OK Build type: Release

Remote Plugins ~
- OK Up to date

terminal ~
- key_backspace (kbs) terminfo entry: `key_backspace=^H`
- key_dc (kdch1) terminfo entry: `key_dc=\E[3~`
- $TERM_PROGRAM="Apple_Terminal"

External Tools ~
- OK ripgrep 14.1.0 (/usr/local/bin/rg)

==============================================================================
nvim-treesitter: require("nvim-treesitter.health").check()

Installation ~
- OK `tree-sitter` found 0.23.0 (parser generator, only needed for :TSInstallFromGrammar)
- OK `node` found v18.20.4 (only needed for :TSInstallFromGrammar)
- OK `git` executable found.
- OK `cc` executable found. Selected from { vim.NIL, "cc", "gcc", "clang", "cl", "zig" }
  Version: Apple clang version 15.0.0 (clang-1500.3.9.4)
- OK Neovim was compiled with tree-sitter runtime ABI version 14 (required >=13). Parsers must be compatible with runtime ABI.

OS Info:
{
  machine = "x86_64",
  release = "23.6.0",
  sysname = "Darwin",
  version = "Darwin Kernel Version 23.6.0: Mon Jul 29 21:13:00 PDT 2024; root:xnu-10063.141.2~1/RELEASE_X86_64"
} ~

Parser/Features         H L F I J
  - awk                 ✓ . . . ✓
  - bash                ✓ ✓ ✓ . ✓
  - c                   ✓ ✓ ✓ ✓ ✓
  - c_sharp             ✓ ✓ ✓ . ✓
  - cmake               ✓ . ✓ ✓ ✓
  - css                 ✓ . ✓ ✓ ✓
  - csv                 ✓ . . . .
  - devicetree          ✓ ✓ ✓ ✓ ✓
  - diff                ✓ . . . ✓
  - dockerfile          ✓ . . . ✓
  - git_config          ✓ . ✓ . ✓
  - git_rebase          ✓ . . . ✓
  - gitattributes       ✓ ✓ . . ✓
  - gitcommit           ✓ . . . ✓
  - gitignore           ✓ . . . ✓
  - gpg                 ✓ . . . ✓
  - html                ✓ ✓ ✓ ✓ ✓
  - http                ✓ . . . ✓
  - java                ✓ ✓ ✓ ✓ ✓
  - javascript          ✓ ✓ ✓ ✓ ✓
  - json                ✓ ✓ ✓ ✓ .
  - latex               ✓ . ✓ . ✓
  - lua                 ✓ ✓ ✓ ✓ ✓
  - markdown            ✓ . ✓ ✓ ✓
  - markdown_inline     ✓ . . . ✓
  - objc                ✓ ✓ ✓ ✓ ✓
  - passwd              ✓ . . . .
  - pem                 ✓ . ✓ . ✓
  - perl                ✓ . ✓ . ✓
  - php                 ✓ ✓ ✓ ✓ ✓
  - printf              ✓ . . . .
  - python              ✓ ✓ ✓ ✓ ✓
  - query               ✓ ✓ ✓ ✓ ✓
  - readline            ✓ . ✓ ✓ ✓
  - regex               ✓ . . . .
  - requirements        ✓ . . . ✓
  - robot               ✓ . ✓ ✓ ✓
  - scss                ✓ . ✓ ✓ ✓
  - sql                 ✓ . . ✓ ✓
  - ssh_config          ✓ ✓ ✓ ✓ ✓
  - strace              ✓ . . . ✓
  - swift               ✓ ✓ . ✓ ✓
  - tmux                ✓ . . . ✓
  - todotxt             ✓ . . . .
  - toml                ✓ ✓ ✓ ✓ ✓
  - typescript          ✓ ✓ ✓ ✓ ✓
  - vim                 ✓ ✓ ✓ . ✓
  - vimdoc              ✓ . . . ✓
  - vue                 ✓ . ✓ ✓ ✓
  - xml                 ✓ ✓ ✓ ✓ ✓
  - yaml                ✓ ✓ ✓ ✓ ✓

  Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections
         +) multiple parsers found, only one will be used
         x) errors found in the query, try to run :TSUpdate {lang} ~

==============================================================================
provider.clipboard: require("provider.clipboard.health").check()

Clipboard (optional) ~
- OK Clipboard tool found: pbcopy

==============================================================================
provider.node: require("provider.node.health").check()

Node.js provider (optional) ~
- Node.js: v18.20.4

- WARNING Missing "neovim" npm (or yarn, pnpm) package.
  - ADVICE:
    - Run in shell: npm install -g neovim
    - Run in shell (if you use yarn): yarn global add neovim
    - Run in shell (if you use pnpm): pnpm install -g neovim
    - You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim

==============================================================================
provider.perl: require("provider.perl.health").check()

Perl provider (optional) ~
- WARNING "Neovim::Ext" cpan module is not installed
  - ADVICE:
    - See :help |provider-perl| for more information.
    - You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim
- WARNING No usable perl executable found

==============================================================================
provider.python: require("provider.python.health").check()

Python 3 provider (optional) ~
- pyenv: Path: /usr/local/Cellar/pyenv/2.4.10/libexec/pyenv
- pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.
- WARNING pyenv: Root does not exist: /Users/hunter/.pyenv
  . Ignoring pyenv for all following checks.
- WARNING No Python executable found that can `import neovim`. Using the first available executable for diagnostics.
- WARNING Could not load Python :
  /usr/local/bin/python3 does not have the "neovim" module.
  /usr/local/bin/python3.12 does not have the "neovim" module.
  python3.11 not found in search path or not executable.
  /usr/local/bin/python3.10 does not have the "neovim" module.
  /usr/local/bin/python3.9 does not have the "neovim" module.
  /usr/local/bin/python3.8 does not have the "neovim" module.
  python3.7 not found in search path or not executable.
  python not found in search path or not executable.
  - ADVICE:
    - See :help |provider-python| for more information.
    - You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim
- Executable: Not found

Python virtualenv ~
- OK no $VIRTUAL_ENV

==============================================================================
provider.ruby: require("provider.ruby.health").check()

Ruby provider (optional) ~
- Ruby: ruby 3.3.4 (2024-07-09 revision be1089c8ec) [x86_64-darwin23]
- WARNING `neovim-ruby-host` not found.
  - ADVICE:
    - Run `gem install neovim` to ensure the neovim RubyGem is installed.
    - Run `gem environment` to ensure the gem bin directory is in $PATH.
    - If you are using rvm/rbenv/chruby, try "rehashing".
    - See :help |g:ruby_host_prog| for non-standard gem installations.
    - You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim

==============================================================================
rainbow-delimiters: require("rainbow-delimiters.health").check()

- No custom configuration; see :help |rb-delimiters-setup| for information.

==============================================================================
vim.lsp: require("vim.lsp.health").check()

- LSP log level : WARN
- Log path: /Users/hunter/.local/state/nvim/lsp.log
- Log size: 431 KB

vim.lsp: Active Clients ~
- GitHub Copilot (id: 1)
    Root directory: nil
    Command: node /Users/hunter/.config/nvim/pack/github/start/copilot.vim/dist/language-server.js --stdio
    Settings: {
      ["github-enterprise"] = {
        uri = vim.NIL
      },
      http = {
        proxy = vim.NIL,
        proxyStrictSSL = vim.NIL
      }
    }
    Attached buffers: 

vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients

vim.lsp: Position Encodings ~
- No buffers contain mixed position encodings

==============================================================================
vim.treesitter: require("vim.treesitter.health").check()

- Nvim runtime ABI version: 14
- OK Parser: awk                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/awk.so
- OK Parser: bash                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/bash.so
- OK Parser: cmake                ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/cmake.so
- OK Parser: css                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/css.so
- OK Parser: csv                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/csv.so
- OK Parser: c_sharp              ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/c_sharp.so
- OK Parser: devicetree           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/devicetree.so
- OK Parser: diff                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/diff.so
- OK Parser: dockerfile           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/dockerfile.so
- OK Parser: gitattributes        ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/gitattributes.so
- OK Parser: gitcommit            ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/gitcommit.so
- OK Parser: gitignore            ABI: 13, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/gitignore.so
- OK Parser: git_config           ABI: 13, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/git_config.so
- OK Parser: git_rebase           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/git_rebase.so
- OK Parser: gpg                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/gpg.so
- OK Parser: html                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/html.so
- OK Parser: http                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/http.so
- OK Parser: java                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/java.so
- OK Parser: javascript           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/javascript.so
- OK Parser: json                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/json.so
- OK Parser: latex                ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/latex.so
- OK Parser: markdown             ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/markdown.so
- OK Parser: objc                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/objc.so
- OK Parser: passwd               ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/passwd.so
- OK Parser: pem                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/pem.so
- OK Parser: perl                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/perl.so
- OK Parser: php                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/php.so
- OK Parser: printf               ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/printf.so
- OK Parser: python               ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/python.so
- OK Parser: readline             ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/readline.so
- OK Parser: regex                ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/regex.so
- OK Parser: requirements         ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/requirements.so
- OK Parser: robot                ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/robot.so
- OK Parser: scss                 ABI: 13, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/scss.so
- OK Parser: sql                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/sql.so
- OK Parser: ssh_config           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/ssh_config.so
- OK Parser: strace               ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/strace.so
- OK Parser: swift                ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/swift.so
- OK Parser: tmux                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/tmux.so
- OK Parser: todotxt              ABI: 13, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/todotxt.so
- OK Parser: toml                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/toml.so
- OK Parser: typescript           ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/typescript.so
- OK Parser: vue                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/vue.so
- OK Parser: xml                  ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/xml.so
- OK Parser: yaml                 ABI: 14, path: /Users/hunter/.local/share/nvim/plugged/nvim-treesitter/parser/yaml.so
- OK Parser: c                    ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/c.so
- OK Parser: lua                  ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/lua.so
- OK Parser: markdown             ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/markdown.so
- OK Parser: markdown_inline      ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/markdown_inline.so
- OK Parser: query                ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/query.so
- OK Parser: vim                  ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/vim.so
- OK Parser: vimdoc               ABI: 14, path: /usr/local/Cellar/neovim/0.10.1/lib/nvim/parser/vimdoc.so

Minimal vimrc that can reproduce this bug.

call plug#begin(stdpath('data') . '/plugged')
Plug 'sainnhe/gruvbox-material'
call plug#end()
colorscheme gruvbox-material

Steps to reproduce this bug using minimal vimrc

  1. Create a file with .sh or .bash as the extension: touch tmp.bash
  2. Open the file using neovim: nvim tmp.bash
  3. Paste the following code into the file:

    #!/usr/bin/env bash
    
    func() {
        kill -9 "$job" > /dev/null 2>&1
        read -r octet1 octet2 octet3 octet4 <<< "$ip"
        clean_exit "1" "" "false"
    }
    
    function func() {
        kill -9 "$job" > /dev/null 2>&1
        read -r octet1 octet2 octet3 octet4 <<< "$ip"
        clean_exit "1" "" "false"
    }
    
    function func {
        kill -9 "$job" > /dev/null 2>&1
        read -r octet1 octet2 octet3 octet4 <<< "$ip"
        clean_exit "1" "" "false"
    }
  4. Save the file: :w
  5. Notice the difference in syntax highlighting between the functions. Screenshot 2024-09-01 at 11 16 22 AM

Expected behavior

When creating a function in Bash, with or without the function keyword, the syntax highlighting is the same as if the code were outside the function.

Actual behavior

When creating a Bash function without the function keyword, some text is highlighted in an "olive green-looking" color instead of the standard "light-yellowish white-looking" color. But when I specify the function keyword, the syntax highlighting works as it should.

Screenshot 2024-09-01 at 11 16 22 AM

antoineco commented 2 months ago

Can this be reproduced with :colorscheme default?

StrangeRanger commented 2 months ago

At least with the default colorscheme for my version of neovim, no:

Screenshot 2024-09-01 at 8 59 48 PM

Note that I reset my init.vim to the minimum settings before using :colorscheme default:

call plug#begin(stdpath('data') . '/plugged')
Plug 'sainnhe/gruvbox-material'
call plug#end()
colorscheme gruvbox-material
antoineco commented 2 months ago

I just tried locally and got the same result as you did.

The behavior comes from Vim's syntax. The Vim syntax has two highlight groups for the body of shell functions:

I haven't yet investigated the reason for this distinction, and I'm not sure why Gruvbox Material defines only shFunctionOne (and why Green instead of Normal), but I'll try to provide you with an answer and possible fix.

As an aside: Tree-sitter is much more accurate that Vim's syntax. Since you seem to have the grammar for Bash installed, is there any reason why you are not using Tree-sitter highlights?

StrangeRanger commented 2 months ago

I wasn't aware that I wasn't using the tree-sitter syntax highlighting. I thought it just as simple as installing the plugin and neovim would default to using it. I'll look into how to enable it. Would using the tree-sitter syntax potentially fix this problem, or is this problem more with the gruvbox-theme?

antoineco commented 2 months ago

Enabling Tree-sitter should be as simple as calling the module's setup() function (a common pattern among Lua plugins). And yes, it should eliminate the problem you are seeing, although we should probably still consider fixing the Vim syntax because Gruvbox Material supports Vim as well as Neovim.

StrangeRanger commented 2 months ago

Enabling Tree-sitter should be as simple as calling the module's setup() function (a common pattern among Lua plugins).

Sweat, thank you for providing that link.

And yes, it should eliminate the problem you are seeing, although we should probably still consider fixing the Vim syntax because Gruvbox Material supports Vim as well as Neovim.

That makes sense. I don't think I can help with much due to my limited knowledge, but please let me know if there is anything I can do to help.

With that said, this question may be out of the scope of the current conversation, but how does neovim-treesitter use gruvbox-material compared to Vim? It obviously uses it differently, as the issue isn't a problem with Treesetter and how some things are colored differently, but I'd like to know how it can do that while using the same code base. Are there just different rules for each "syntax parsing method" provided by Vim and Treesetter?

antoineco commented 2 months ago

Happy to clarify: all a (Neo)vim colorscheme does is associate a set of well known highlight groups with a certain combination of background/foreground colors and, optionally, a style such as bold or italic.

Both of these syntaxes do the same thing: they parse the buffer and associate certain segments of it (e.g. strings, variables, functions, ...) with a suitable highlight group. By associating highlight groups for both Vim and Tree-sitter to concrete colors, Gruvbox Material can effectively support both highlight methods.

As you can imagine, Tree-sitter is much easier to get right due to its strong conventions which are lacking in legacy Vim. Most modern colorschemes don't bother providing per-language customizations for Vim, and I can't blame them for not doing it 🙂

StrangeRanger commented 2 months ago

Oh, ok. That makes sense. Thanks for taking the time to explain that.

antoineco commented 1 month ago

@sainnhe do you happen to remember why shFunctionOne links to GreenBold, while shFunctionTwo is cleared?

This looks like a bug to me. When I clear the highlight group, shell functions in the form name() { ... } are highlighted the same way as functions in the form function name { ... }, which I believe is expected. I couldn't spot any other side effect, and looking at Vim's syntax/bash.vim doesn't quite help me because I'm unfamiliar with the regular expression style used by Vim.

Note also that I haven't found customizations for this highlight group in other Vim color schemes, outside of Everforest, Edge and Sonokai (I checked ~10 of the most popular ones).

sainnhe commented 1 month ago

@antoineco This seems to be a bug, and I also forget why I linked shFunctionOne to GreenBold. Looks like this highlight link can be safely removed.