- Highlights Zsh syntax as a command line is edited.
- Provides command-specific chroma highlighters for tools such as Git, Docker, grep, and make.
- Supports shipped and user-defined themes through
fsh_theme. - Inspects command-specific highlighters and their health through
fsh_chroma. - Highlights nested command substitutions, arithmetic, strings, paths, and shell control structures.
- Zsh 5.8 or newer
- An interactive Zsh Line Editor session for live highlighting
- Project identifier:
fsh - Authoritative entrypoint:
F-Sy-H.plugin.zsh - Public functions:
fsh_chroma,fsh_theme, andfsh_plugin_unload - Public configuration context:
:fsh:config - Autoload paths:
functions/,completions/, and the privatechroma/
The plugin has no public aliases or public parameters. Persistent implementation
state and callbacks use the private _fsh_ prefix. Native completion naming is
the required exceptions: completions for fsh_chroma and fsh_theme are
_fsh_chroma and _fsh_theme.
F-Sy-H.plugin.zshis the only entrypoint.lib/contains private code sourced eagerly by the entrypoint and is not onfpath.functions/contains one autoload function per file.chroma/contains private command-specific autoload functions.completions/contains native completion functions. The plugin makes this directory available onfpathbut never callscompinit.themes/andshare/contain declarative INI data.tests/integration/andtests/unit/contain executable integration profiles and ZUnit specifications, respectively.
- Public functions:
fsh_chroma,fsh_theme, andfsh_plugin_unload - Private persistent functions and parameters: names beginning with
_fsh_ - Direct module requests:
zsh/parameter,zsh/system, optionalzsh/nearcolor, and interactive-onlyzsh/zleparameter - Hook:
_fsh_preexec_hookinpreexec_functions - Widgets:
_fsh_check_path_handler_widget,_fsh_widget_*wrappers, and temporaryfsh-orig-*saved-widget names
The unload function tracks modules loaded transitively during initialization and lazy plugin operations. It only claims modules that were not loaded before the plugin.
zi light z-shell/F-Sy-HZi is the reference plugin manager for Z-Shell documentation and validation.
git clone https://github.com/z-shell/F-Sy-H.git ~/path/to/f-sy-h
source ~/path/to/f-sy-h/F-Sy-H.plugin.zshManagers that source conventional *.plugin.zsh entrypoints can load
z-shell/F-Sy-H. Detailed manager-specific examples live in the
F-Sy-H wiki guide.
This layout intentionally uses the Zsh Plugin Standard version 2 contract as a clean interface. Existing configurations need these changes:
- Replace
fast-themeand thef-sy-halias withfsh_theme. - Replace
FAST_WORK_DIRwithzstyle ':fsh:config' work-dir .... - Replace
ZSH_HIGHLIGHT_MAXLENGTHwithzstyle ':fsh:config' max-length .... - Replace
FAST_THEME_MANAGER_DISABLED=1withzstyle ':fsh:config' theme-manager disabled. - Replace direct mutation of plugin globals with the documented settings below.
- Reapply a theme with
fsh_theme; executable legacy theme cache files are not loaded.
Legacy functions, aliases, parameters, and executable cache formats are not retained as a second compatibility interface.
F-Sy-H is not configuration-compatible with zsh-syntax-highlighting. Remove
ZSH_HIGHLIGHT_STYLES and ZSH_HIGHLIGHT_HIGHLIGHTERS configuration when
switching plugins. F-Sy-H does not read or translate either parameter. If one
is already declared when F-Sy-H loads, the plugin prints one migration
diagnostic without reading or changing its values.
Zsh requires associative arrays to be declared before assigning an element. For example, zsh-syntax-highlighting documents this sequence:
typeset -A ZSH_HIGHLIGHT_STYLES
ZSH_HIGHLIGHT_STYLES[comment]='fg=201'Without the typeset -A line, Zsh reports assignment to invalid subscript range at the assignment itself. If that assignment appears before the F-Sy-H
source or manager command, F-Sy-H has not run yet and cannot intercept the
error. Remove the legacy block instead of moving it after the F-Sy-H load.
Replace the legacy controls as follows:
| zsh-syntax-highlighting | F-Sy-H replacement |
|---|---|
ZSH_HIGHLIGHT_MAXLENGTH=1000 |
zstyle ':fsh:config' max-length 1000 |
ZSH_HIGHLIGHT_HIGHLIGHTERS=(main) |
Main syntax highlighting is integrated and always active. |
Add brackets to ZSH_HIGHLIGHT_HIGHLIGHTERS |
zstyle ':fsh:config' bracket-highlighting enabled |
Add pattern, regexp, cursor, root, or line |
No direct equivalent. Remove the entry or implement the behavior outside F-Sy-H. |
ZSH_HIGHLIGHT_STYLES[...] |
Copy and edit an F-Sy-H INI theme, then apply it with fsh_theme. |
Many common main highlighter style names map directly to F-Sy-H theme keys:
| zsh-syntax-highlighting style | F-Sy-H theme key |
|---|---|
unknown-token, reserved-word, alias, suffix-alias, global-alias |
Same name |
builtin, function, command, precommand, hashed-command |
Same name |
commandseparator, path, path_pathseparator, globbing |
Same name |
history-expansion, single-hyphen-option, double-hyphen-option |
Same name |
back-quoted-argument, single-quoted-argument, double-quoted-argument |
Same name |
dollar-quoted-argument, assign, redirection, comment, default |
Same name |
Other zsh-syntax-highlighting keys do not have a one-to-one mapping. F-Sy-H uses more specific keys for arithmetic, loops, case blocks, here strings, bracket levels, directories, subcommands, and command option arguments. Start from a shipped theme so those F-Sy-H-specific styles retain valid fallbacks:
theme_dir=${XDG_CONFIG_HOME:-$HOME/.config}/f-sy-h
mkdir -p -- "$theme_dir"
fsh_theme --copy-shipped-theme default "$theme_dir/migrated"
# Edit "$theme_dir/migrated.ini", then apply it:
fsh_theme "$theme_dir/migrated.ini"Theme INI values use red,bold for a foreground and bg:blue for a
background. The corresponding zsh-syntax-highlighting forms are fg=red,bold
and bg=blue. Run fsh_theme --help for theme commands and see the
configuration section below for the full :fsh:config interface.
All ordinary settings use :fsh:config. Set them before loading the plugin:
zstyle ':fsh:config' work-dir "${XDG_CACHE_HOME:-$HOME/.cache}/f-sy-h"
zstyle ':fsh:config' max-length 1000
zstyle ':fsh:config' theme-manager enabled
zstyle ':fsh:config' bracket-highlighting enabled
zstyle ':fsh:config' path-blocklist '/private/*' '/mnt/slow/**'
zstyle ':fsh:config' chroma-opt-in vim
zstyle ':fsh:config' chroma-cache-seconds 5
zstyle ':fsh:config' chroma-timeout-seconds 2
zi light z-shell/F-Sy-HThe settings are:
work-dir: scalar path, default${XDG_CACHE_HOME:-$HOME/.cache}/f-sy-h.max-length: non-negative integer, default1000.theme-manager: boolean-like scalar, defaultenabled.bracket-highlighting: boolean-like scalar, defaultenabled.path-blocklist: array of Zsh patterns excluded from path probing, empty by default.chroma-opt-in: array containingvim,which, or both, empty by default. Thevimchroma reads.viminfoand displays recent files. Thewhichchroma runs multiple command-discovery tools while highlighting.chroma-cache-seconds: non-negative lifetime for asynchronous chroma lookup results, default5.chroma-timeout-seconds: positive time budget for an asynchronous chroma worker, default2. A worker that exceeds it is disabled for the session and reports one ZLE warning.
For boolean-like settings, disabled, false, no, off, and 0 disable
the feature; any other value enables it.
At or below max-length, edits to independent simple command lists can reuse
highlighting before a parser-confirmed top-level semicolon or newline. Quoting,
redirections, assignments, aliases, chroma, control structures, changed theme
or shell context, and other ambiguous input use a full parse. Bracket and
string highlighting still scan the complete buffer. Buffers above the limit
remain unhighlighted rather than switching to a degraded highlighting mode.
Theme file examples and additional usage guidance are documented in the wiki guide.
List registered command highlighters and classify files that are not directly registered:
fsh_chroma listThe KIND column distinguishes command-specific dedicated handlers from the
shared generic fallback. The generic handler highlights the first non-option
word as a subcommand, but it does not validate that word or parse
command-specific options. A generic registry entry therefore does not represent
the same depth of coverage as a dedicated handler. STATUS reports whether the
target is ready, missing, or intentionally disabled.
Check registry reachability, declarative definitions, active theme styles, and session-disabled asynchronous lookups:
fsh_chroma doctorAdd a nine-run median of end-to-end highlighting time for one command line:
fsh_chroma doctor --sample 'docker image rm deadbeef'The sample runs outside active ZLE, so it does not start asynchronous lookup
workers. It measures the complete highlighting pass, not isolated chroma
function time. doctor returns status 0 when healthy and 1 when it finds a
problem; invalid command usage returns status 2.
List available themes:
fsh_theme --listThe listing includes each theme's intended background, whether its palette is fixed or terminal-owned, and a short description.
Compare every shipped theme using the same highlighted sample:
fsh_theme --galleryPreview a theme on the next command line:
fsh_theme --test cleanPreviewing does not change the active theme or saved theme files. Inspection
commands (--help, --info, --palette, --list, --gallery, and --show)
also leave the theme work directory untouched. The gallery preserves an
existing one-shot preview as well as the active and saved themes.
fsh_theme --show reports both the active and session-startup theme names and
source paths.
Apply a theme:
fsh_theme cleanIf Tinty or another Base16 manager sets your terminal's ANSI 16-color palette, select the shipped adaptive theme:
fsh_theme base16This theme uses the terminal-owned foreground, background, and ANSI colors, so changing the terminal scheme also changes F-Sy-H without generating or downloading another theme file.
Interactive loading:
- adds
functions/,completions/, andchroma/tofpathwhen absent; - wraps existing ZLE widgets and creates the path-check handler widget;
- registers
_fsh_preexec_hookinpreexec_functions; - loads the Zsh modules needed by highlighting; and
- defines the documented functions and private state above.
Non-interactive loading defines the shell API but does not change widgets or
install the preexec hook. Repeated sourcing is a no-op after a successful
load.
fsh_plugin_unload removes plugin-owned hooks, widgets, functions,
parameters, modules, and fpath entries. It restores state captured
before the first load only while the installed value remains unchanged. A
widget, function, or parameter changed after loading is preserved.
Loading performs no network request and does not create the cache directory.
The explicit fsh_theme command creates storage only when it needs to write
theme state. Preview and inspection do not create it. Saved state uses
current_theme.ini, theme_overlay.ini, and secondary_theme.local.ini.
These files are parsed as data. Legacy writable *.zsh theme caches are
deliberately ignored and never sourced.
From the repository root:
zsh -f -n F-Sy-H.plugin.zsh lib/*.zsh functions/* completions/* chroma/*
zsh -f tests/integration/test-plugin-entrypoint.zsh
zsh -f tests/integration/test-plugin-lifecycle.zsh noninteractive
zsh -f -i tests/integration/test-plugin-lifecycle.zsh interactive
zsh -f tests/integration/test-function-completion.zsh
zsh -f tests/integration/test-git-chroma-regions.zsh
zsh -f tests/integration/test-passive-safety.zsh
zsh -f tests/integration/test-hostile-autoloads.zsh
zsh -f tests/integration/test-highlight-performance.zsh
zsh -f tests/integration/test-theme-persistence.zsh
zsh -f tests/integration/test-chroma-registry.zsh
zsh -f tests/integration/test-chroma-regions.zsh
zsh -f tests/integration/test-async-chroma.zsh
zsh -f tests/integration/test-theme-validator.zsh
zsh -f tools/validate-themes.zsh
zunitThe highlight-performance profile measures nine parses of representative, delimiter-free single commands at 173 and 1,000 characters after one warm-up run. Pull-request CI compares the medians with the base revision on the same runner and updates a PR comment with the relative difference. Hardware timing does not gate the build; empty highlighting, steady-state lifecycle refreshes, and failure to skip a buffer above the default limit remain test failures.
tools/validate-themes.zsh validates all shipped themes by default and accepts
explicit INI paths as arguments. It emits one JSON Lines record per result or
diagnostic using schema fsh-theme-validation/v1, and exits non-zero if any
record has status set to error. Each record's nearcolor256 object maps
the theme's distinct truecolor style literals to the xterm-256 indices selected
by the installed Zsh zsh/nearcolor module. It is empty when the theme has no
truecolor styles; an unavailable module produces a structured
nearcolor-unavailable error.
Every shipped theme declares a [theme] rendering contract and a one-line
description. Fixed-palette themes use palette = xterm-256 with exact
foreground and background #rrggbb values. Their resolved ordinary styles
must reach a contrast ratio of 4.5:1; unknown-token, incorrect-subtle, and
matherr must reach 7:1. palette = terminal-ansi16 is adaptive and restricts
colors to terminal-owned ANSI indices 0 through 15 instead of claiming a fixed
contrast ratio. Rendering metadata and descriptions remain optional for
external themes, preserving existing user themes. A supplied rendering
contract receives the same validation as a shipped theme.
That contract also keeps semantically opposed styles distinguishable. The
validator compares resolved rendering state rather than raw INI text, so named
and indexed equivalents, backgrounds, attributes, none, and reverse are
canonicalized before comparison. Single- and double-hyphen options, string and
numeric option arguments, and subcommands and string option arguments must
differ. Command and builtin styles, function and command styles, and alias and
suffix-alias styles may intentionally share a rendering. Overlays are checked
as partial customizations, so standalone overlay validation does not enforce
these pair relationships against an unknown base theme.
Fixed-palette themes also keep correct-subtle and incorrect-subtle
separated under the published
Machado, Oliveira, and Fernandes
severity-1.0 protanopia, deuteranopia, and tritanopia simulation matrices. The
validator converts each simulated foreground and background pair to
CIE 1976 Lab*
and requires at least one channel to retain a project-defined distance of
20.0. Terminal-owned ANSI palettes and standalone overlays cannot make a fixed
color claim, so this check does not apply to them. This regression guard is
not a WCAG conformance claim or a substitute for a
non-color correctness cue.
The ZUnit command requires the repository's pinned ZUnit toolchain.
Contributions integrate on main. F-Sy-H is consumed directly from Git;
version tags identify reviewed snapshots and do not introduce a separate
package registry.
Contributions follow the Z-Shell organization guidance. This project is distributed under the terms in LICENSE.
