Skip to content

update syntax highlighting to more closely match existing languages #110

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

Techatrix
Copy link

@Techatrix Techatrix commented Jun 22, 2025

The current syntax highlighting that is provided for various keywords is in complete disconnect from any conventions established by syntax highlighting configurations from other languages. Here are some examples:

  • var, const, comptime and threadlocal are mapped to the "Function" highlighting group by default
  • defer, errdefer and extern are mapped to the "Macro" highlighting group by default
  • volatile, linksection and allowzero are mapped to the "Type" highlighting group by default
  • return, break and continue are mapped to the "Special" highlighting group by default

Having looked at the syntax highlighting of languages like C, C#, Rust, JavaScript, Java and Go, I haven't found a single languages with as many unusual choices as this plugin. My guess is that this syntax highlighting has been developed with some kind of flawed methodology of choosing whatever gives the desired highlighting on some specific colorscheme.

Syntax highlighting configurations and colorschemes do not existing in isolation. We should try our best to match existing configuration of other languages and only deviate from them if we have a good reason for doing so. I ended up changing the configuration for many different keywords. The reasoning for these changes is explained in the individual commit messages.

Here is how these changes affect some popular colorschemes:

Show Images
Before After
folke/tokyonight.nvim folke/tokyonight.nvim
neovim-tokyonight-old neovim-tokyonight-new
morhetz/gruvbox morhetz/gruvbox
neovim-gruvbox-old neovim-gruvbox-new
catppuccin/catppuccin catppuccin/catppuccin
neovim-catppuccin-old neovim-catppuccin-new
navarasu/onedark.nvim navarasu/onedark.nvim
neovim-onedark-old neovim-onedark-new
Neovim's "vim" colorscheme Neovim's "vim" colorscheme
neovim-vim-old neovim-vim-new
const some_const: type = anyframe;
extern var some_var: u32;

pub const some_pub: []const u8 = "hello\\nworld!";
threadlocal var another_var: [*]allowzero align(8) u8 = undefined;

/// Some comment
const SomeStruct = packed struct {
    foo: u32,
    usingnamespace SomeStruct;
    fn copy(self: @This()) SomeStruct {
        return self;
    }
};

fn func(some: anytype) error{OutOfBrain}!void {
    var foo: u32 = 5;
    blk: while (some != 5) : (foo += 2) {
        if (1 != 2 or 2 != 1) {
            break;
        } else {
            continue :blk;
        }
    }
    errdefer comptime unreachable;
    return {};
}

test func {
    const result: SomeStruct = await func() catch unreachable;
    defer _ = result.copy();
    nosuspend {
        @import("std").debug.print("Hello {s}\\n", .{"World"});
    }
    return error.SkipZigTest;
}

pub export fn fanc(comptime T: type) callconv(.c) *anyopaque {
    inline for (0..5) |i| {
        switch (i) {
            .bar => |params| try T.member(params) orelse return,
            else => {},
        }
    }
}

noinline fn syscall1(noalias number: usize, arg1: usize) usize {
    return asm volatile ("syscall"
        : [ret] "={rax}" (-> usize),
        : [number] "{rax}" (number),
          [arg1] "{rdi}" (arg1),
        : "rcx", "r11"
    );
}

Techatrix added 10 commits July 11, 2025 10:03
This change shouldn't have any functional changes but should make the
next commits more readable.
The previous mapping made no sense. The mapping of threadlocal and
comptime will be changed in future commits.

As a reference I used the builtin syntax highlighting from neovim for
the following languages that have similar keywords to Zig's `var` and
`const`:

- Rust: `let`
- Go: `var`
- Javascript: `var`,`let`, `const`

They all map their keywords to the Keyword syntax highlighting group.
All control flow keywords are now mapped to either Keyword, Statement or
Conditional. Previously some of them would be mapped to Special, PreProc
or Macro.

As a reference I used the builtin syntax highlighting from neovim for
the following languages that have similar control flow keywords to Zig:

- Rust:
  - Keyword: return break continue
  - Conditional: match if else
  - Repeat: loop while (actually maps to Conditional)
- C
  - Statement: goto break return continue asm
  - Conditional: if else switch
  - Repeat: while for do
- C#: Conditional, Repeat
  - Conditional: else if switch
  - Repeat: break continue do for foreach goto return while
- JS
  - Statement: return with await yield
  - Conditional: if else switch break continue
  - Repeat: while for do in of
- Go
  - Statement: defer go goto return break continue fallthrough
  - Conditional: if else switch select
  - Repeat: for range

- Zig (old)
  - Macro: defer errdefer
  - PreProc: asm
  - Special: return break continue
  - Conditional: if else switch
  - Repeat: while for
  - Keyword: and or orelse
- Zig (new)
  - Statement: return break continue asm defer errdefer and or orelse
  - Conditional: if else switch
  - Repeat: while for

The try and catch keywords will be adjusted in a future commit.
The nosuspend keyword is syntactically different from the other keywords
so this mapping isn't quite right.
- volatile, linksection and allowzero obviously aren't types.

The differentiation between what is mapped to "StorageClass" or
"PreProc" isn't very clear. At least it's less scattered than the
previous mappings.
As a reference I used the syntax highlighting that neovim uses for the
following languages that have similar keywords to Zig's `pub`:

- C++:
  - Statement: public protected private
- Rust:
  - Keyword: pub
- C#:
  - StorageClass: internal private protected public
- JS:
  - Keyword: private protected public
The `error` keyword has two different appearances in sytnax that should
be differentiated between. For now, this keyword remains mapped twice
which isn't correct.

I have tested these changes with the following colorschemes:

- folke/tokyonight.nvim
- morhetz/gruvbox
- catppuccin/nvim
- joshdick/onedark.vim
- Neovim's "vim" colorscheme

All of themes appear to map "Exception" to "Statement" which matches
well with other keywords in said group.
The "Keyword" group now only has keywords that introduce new
declarations
Previously builtin functions were the only thing mapped to "Statement".
Now, some control flow keywords are mapped to "Statement", so builtin
functions are instead mapped to the more reasonable "Function" group.

I believe that "Statement" was previously picked to ensure that builtin
functions get a color that would stand out and be unique. Function may
not get an equally prominent color but still be unique (usually).

Depending on how this affects various colorschemes, it may also make
sense to map builtin functions to a different group like "Special".
Most colorschemes will still map "Boolean" to "Constant".
@Techatrix Techatrix force-pushed the syntax-highlighting branch from 065d3f7 to aa241a5 Compare July 11, 2025 08:08
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.

1 participant