([n]vim[f]ade)
This plugin was created to help keep your attention focused on the active buffer especially in scenarios where you might have many windows open at the same time.
Previously Vimade faded just the inactive buffers. Vimade has now transitioned into a plugin that is fully customizable and you can highlight any window/buffer however you see fit. The old "just fade/dim" functionality is a small subset of the new features!
For Neovim 0.8.0+ nothing. This plugin supports a lua-only code path, you are all set!
Vim7.4+ and Neovim < 0.8.0 require Python or Python3 support. For older versions of Neovim, you will need to install pynvim
.
- Fade or highlight windows or buffers.
- Link windows so that change together (e.g. diffsplit)
- Blocklist
- Custom tints
- Prebuilt recipes
- Fully customizable (see styles and recipes for some of the possibilities)
- Animated transitions.
- Automatically adjusts to configuration changes.
- Helpers to make inactive built-ins look better
- Supports 256 color terminals and termguicolors.
- Sub milliscond Lua performance and highly optimized Python logic for Vim.
- Preconfigured commands such as (VimadeEnable, VimadeDisable, VimadeRedraw, etc)
- Supports all versions of Neovim and Vim 7.4+
- Vim Documentation/Help
- Some good stuff
- Code cleanup
- Tests
Installation -
Any plugin manager will work.
::vimscript::plug::
Plug 'TaDaa/vimade'
::lua::lazy::
require('lazy').setup({spec = {'tadaa/vimade'}})
For Lua users:
This is just here to remind you that you don't need to do anything!
For Python users:
If you are using vim or older neovim and want to configure using python, you will need to bind your setup to Vimade#PythonReady
. This ensures that Vimade has been added to the python path before your configuration runs. Here's an example that sets up the Minimalist recipe.
Vimade#PythonReady
. This ensures that Vimade has been added to the python path before your configuration runs. Here's an example that sets up the Minimalist recipe.function! SetupMyVimadeConfig()
python << EOF
from vimade import vimade
from vimade.recipe.minimalist import Minimalist
vimade.setup(**Minimalist(animate=True))
EOF
endfunction
au! User Vimade#PythonReady call SetupMyVimadeConfig()
Lazy loading
-
For Lua/Neovim, you can use lazy.nvim and your event of choice:
::lua::lazy::
require('lazy').setup({spec = {'tadaa/vimade', event = 'VeryLazy'}})
-
For full control and Vim, enable
vimade.lazy
and then callvimade#Load()
. Here's an example:::vimscript::
let g:vimade = {} let g:vimade.lazy = 1 au WinEnter * ++once call vimade#Load()
Basic tutorial
There are a number of ways to specify the configuration for Vimade . Most users will be interested in manipulating the fadelevel and/or tint. Vimade can be configured via vimscript if you prefer a general config that is compatibile with both Neovim and Vim. It can also be configured with Lua and Python if you prefer a specific config or want an advanced configuration that includes animations, recipes, or conditional functions.
If you are configuring Vimade directly in your vimrc, add the following at the start:
::vimscript::
let g:vimade = {}
The initialization above will ensure that you have a vimade object initialized regardless of where you need it. Vimade will initialize its own if it doesn't find one. This object is automatically extended with the default values, so don't worry about adding every option.
Now you can start customizing vimade:
::vimscript::
let g:vimade.fadelevel = 0.5
Simple right? the above code changes the opacity. You can choose any value between 0 and 1. You can change any option at any time and Vimade will automatically react to those changes.
Let's add a blue tint:
::vimscript::
let g:vimade.tint = {'fg':{'rgb':[0,0,255], 'intensity': 0.5}}
You should notice that your text color has changed. By default tint is applied before fade, but don't worry you can change that but that's going to be in a later section (styles not documented yet).
Let's make the above example a bit more complicated, suppose we have a filetree that we don't want to dim as extremely as our other windows. You may remember that I said we need to configure functions directly in python or lua, so let's take a look:
::lua::
require('vimade').setup{
fadelevel = function(style, state)
if style.win.buf_opts.syntax == 'nerdtree' then
return 0.8
else
return 0.4
end
end}
::python::
from vimade import vimade
vimade.setup(
fadelevel = lambda style, state:
0.8 if style.win.buf_opts['syntax'] == 'nerdtree'
else 0.4)
Both languages use the same syntax and logic for configuration.
[!Note] Advanced configurations in python and lua are treated as overlays, whatever you pass through the setup functions will overlay on top of your vimscript configuration. This means you won't be able to do an advanced configuration, then override it with a vimscript configuration after. You'll need to unset the advanced configuration first, which can be done as seen below
::lua::
-- sets the overlay back to empty
require('vimade').setup{}
::python::
from vimade import vimade
# sets the overlay back to empty
vimade.setup()
You now know the basics for configuring Vimade!
Preparing a transparent terminal
When using a transparent terminal, your Normal highlights are set guibg=NONE
and Vimade won't know what the exact target colors. In this scenario,
Vimade will assume the target is black
or white
depending on background settings.
For decent transparent color accuracy, you can set basebg
to a good target value. If you already know the color, you
you can either manually calculate a reasonable value or perform the following steps if it is unknown:
-
Prepare a pure
white
background (it must be exactly#FFFFFF
). -
Place your terminal over the background
-
Use a color picker tool and obtain the exact color value of your terminal. This value is the color code that your terminal considers transparent.
-
Set
fadebg
to whatever the color value is in your Vimade config. For example:::vimscript::
let g:vimade.basebg=[11,11,11]
::lua::
require('vimade').setup{basebg={11,11,11}}
::python::
from vimade import vimade vimade.setup(basebg=[11,11,11])
Highlight by active buffers or windows
Vimade fades buffers by default. This is the primary and legacy behavior of this plugin. Some users may prefer fading by windows, toggling between windows and buffers, or creating their own conditions for determining when to fade or highlight a buffer. These are all possible.
Most users should try each options below to see what they like best. For most, there are inherit benefits to fading based on buffers as its easier to see which windows are impacted by your edits or which windows you can cleanup.
::vimscript::
let g:vimade.ncmode = 'buffers'
let g:vimade.ncmode = 'windows'
Many users will already be familiar with the builtin Neovim highlight: NormalNC, which lets you change the Normal highlight on inactive windows. But what if you want to change the background color for inactive buffers, not just windows? Well, you are in luck, Vimade can do this too (and yes for Vim as well)!
::vimscript::
let g:vimade.ncmode = 'buffers'
let g:vimade.tint = {'bg': {'rgb': [0,0,0], 'intensity': 0.3}}
The code above adds additional darkness to the background of only the inactive buffers. See the image below for the result:
Tinting
Sorry, tutorial not ready yet! See config options for usage.
Blocklists and linking
Sorry, tutorial not ready yet! See config options for usage.
Style modifiers
Styles are the core functionality of Vimade. Each style decides how to manipulate the highlighting process based on their own input. Styles can be combined, nested, or transpose other styles, it's up to the user to decide how they should be used together (and what order they should be in)!
There are 4 core styles
Fade
Fades each window based on the value
(also referred to as fadelevel
). Colors are modified against the
background color.
value
(also referred to as fadelevel
). Colors are modified against the
background color.::lua::
local Fade = require('vimade.style.fade').Fade
vimade.setup{
style = {
Fade{value = 0.4}
}
}
::python::
from vimade import vimade
from vimade.style.fade import Fade
vimade.setup(style = [
Fade(value = 0.4)
])
option | values/type | default | description |
---|---|---|---|
value |
number function(style,state)=>number |
nil |
The target fadelevel. Value ranges from 0 to 1 , where 0 is completely faded and 1 is unfaded. |
tick |
function()=>void |
nil |
A function that is run once per frame. Useful if you need to do expensive pre-computation that shouldn't occur once per-window. |
Tint
Tints each window based on fg
, bg
, and sp
inputs.
fg
, bg
, and sp
inputs.::lua::
local Tint = require('vimade.style.Tint').Tint
vimade.setup{
style = {
Tint{
value = {
fg = {rgb = {0,0,0}, intensity = 0.5},
bg = {rgb = {0,0,0}, intensity = 0.5},
sp = {rgb = {0,0,0}, intensity = 0.5},
}
}
}
}
::python::
from vimade import vimade
from vimade.style.tint import Tint
vimade.setup(style = [
Tint(value = {
'fg': { 'rgb': [0,0,0], 'intensity': 0.5 },
'bg': { 'rgb': [0,0,0], 'intensity': 0.5 },
'sp': { 'rgb': [0,0,0], 'intensity': 0.5 },
})
])
option | values/type | default | description |
---|---|---|---|
value |
function(style,state)=any (functions can be used for any part of the tint config object) |
nil |
The target tint colors. Intensity is the inverse of fadelevel. 1 is full intensity, while 0 is not applied. |
tick |
function()=>void |
nil |
A function that is run once per frame. Useful if you need to do expensive pre-computation that shouldn't occur once per-window. |
Include
Runs nested style modifiers when the highlight is included in the value
.
value
.::lua::
local Fade = require('vimade.style.fade').Fade
local Include = require('vimade.style.include').Include
vimade.setup{
style = {
Include{
value = ['WinSeparator', 'VertSplit', 'LineNr', 'LineNrAbove', 'LineNrBelow'],
style = {
Fade { value = 0.4 }
}
}
}
}
::python::
from vimade import vimade
from vimade.style.fade import Fade
from vimade.style.include import Include
vimade.setup(style = [
Include(
value = ['Normal', 'Comment'],
style = [
Fade(value = 0.4)
]
)
])
option | values/type | default | description |
---|---|---|---|
value |
string[] |
nil |
The list of highlight names that the nested styles will execute modifies on. |
style |
Style[] |
nil |
The list of styles that are run when highlights are included. |
tick |
function()=>void |
nil |
A function that is run once per frame. Useful if you need to do expensive pre-computation that shouldn't occur once per-window. |
Exclude
Runs nested style modifiers when the highlight is not included in the value
.
value
.::lua::
local Fade = require('vimade.style.fade').Fade
local Exclude = require('vimade.style.exclude').Exclude
vimade.setup{
style = {
Exclude{
value = ['WinSeparator', 'VertSplit', 'LineNr', 'LineNrAbove', 'LineNrBelow'],
style = {
Fade { value = 0.4 }
}
}
}
}
::python::
from vimade import vimade
from vimade.style.fade import Fade
from vimade.style.exclude import Exclude
vimade.setup(style = [
Exclude(
value = ['Normal', 'Comment'],
style = [
Fade(value = 0.4)
]
)
])
option | values/type | default | description |
---|---|---|---|
value |
string[] |
nil |
The list of highlight names that the nested styles will execute modifies on. |
style |
Style[] |
nil |
The list of styles that are run when highlights are included. |
tick |
function()=>void |
nil |
A function that is run once per frame. Useful if you need to do expensive pre-computation that shouldn't occur once per-window. |
Animations
Vimade supports animations through value functions. The section below will look at using a custom animation value within a style, so please read the style section before proceeding!
Animations are functions that mutate values over time. Vimade includes a number of helpers that alter the interpolation process. Animations can only be added using lua or python.
Let's look at an example:
::lua::
local Fade = require('vimade.style.fade').Fade
local animate = require('vimade.style.value.animate')
require('vimade').setup{style = {
Fade {
value = animate.Number {
start = 1,
to = 0.2
}
}
}}
::python::
from vimade import vimade
from vimade.style import fade
from vimade.style.value import animate
vimade.setup(style = [
Fade(value = animate.Number(
start = 1,
to = 0.2,
)),
])
The example above uses animate.Number
to fade inactive windows from no-fade start = 1
to almost completely faded to = 0.2
.
The animation can be further customized by overriding any of the default values:
::lua::
local Fade = require('vimade.style.fade').Fade
local direction = require('vimade.style.value.direction')
local ease = require('vimade.style.value.ease')
local animate = require('vimade.style.value.animate')
require('vimade').setup{style = {
Fade {
value = animate.Number {
start = 1,
to = 0.2,
direction = direction.IN_OUT,
ease = ease.OUT_BOUNCE,
duration = 1000,
delay = 100,
}
}
}}
::python::
from vimade import vimade
from vimade.style import fade
from vimade.style.value import animate
from vimade style.value import direction
from vimade style.value import ease
vimade.setup(style = [
Fade(value = animate.Number(
start = 1,
to = 0.2,
direction = direction.IN_OUT,
ease = ease.OUT_BOUNCE,
duration = 1000,
delay = 100,
)),
])
Every value type can be animated included tints and nested values in complex objects. See the recipe source for more examples.
option | values/type | default | description |
---|---|---|---|
start |
any function(style,state)=>any |
nil |
The starting value that the animation begins at. If direction=IN_OUT , then the starting value is only used one time when the value is uninitialized. |
to |
any function(style,state)=>any |
nil |
The ending value that the animation ends at. |
direction |
IN OUT IN_OUT |
OUT |
These are specialized functions and MUST be used from the exported vimade.style.value.direction enum. OUT is a outward animation, which should typically be associated with "leaving" something. IN is an inward animation that should be associated with "entering". IN_OUT tracks the value and performs both IN and OUT behaviors. |
ease |
LINEAR OUT_QUART IN_QUART IN_OUT_QUART IN_CUBIC OUT_CUBIC ... |
OUT_QUART |
These are functions and can be used from vimade.style.value.ease . You can also use your own custom function(time)=>percent_time . Easing functions change the animation behavior by mutating percent_time . See source for examples: lua | python. |
duration |
number function(state,state)=>number |
300 |
The duration of the animation in milliseconds. |
delay |
number function(style,state)=>number |
0 |
How long to wait before starting the animation. |
Recipe: Default
This recipe is enabled by default, but you can re-apply it with additional customizations (e.g. animations). You can only enable recipes through a configuration overlay (no vimscript).
::lua:: source (see here for additional params)
local Default = require('vimade.recipe.default').Default
require('vimade').setup(Default(animate=true))
::python:: source (see here for additional params)
from vimade import vimade
from vimade.recipe.default import Default
vimade.setup(**Default(animate=True))
Recipe: Minimalist
This recipe hides low value built-in highlights on inactive windows such as number column and end of buffer highlights. Greatly reduces visibility of WinSeparator on inactive windows.
::lua:: source (see here for additional params)
local Minimalist = require('vimade.recipe.minimalist').Minimalist
require('vimade').setup(Minimalist{animate = true})
::python:: source (see here for additional params)
NOTE: For vim users with wincolor, minimalist will link the no_visibility_highlights
to Normal
so that they can completely fade-out per-window.
from vimade import vimade
from vimade.recipe.minimalist import Minimalist
vimade.setup(**Minimalist(animate = True))
Configuration options for lua, python, and vimscript
option | values/type | default | description |
---|---|---|---|
renderer |
'auto' 'python' 'lua' |
'auto' |
auto automatically assigns vim users to python and detects if neovim users have the requires features for lua. For neovim users on lua mode, the python logic is never run. Neovim users with missing features will be set to python and need pynvim installed. |
ncmode |
'windows' 'buffers' |
'buffers' |
highlight or unhighlight buffers or windows together |
fadelevel |
float [0-1] function(style,state)=>float |
0.4 |
The amount of fade opacity that should be applied to fg-text (0 is invisible and 1 is no fading) |
tint |
When set via lua or python, each object or number can also be a function that returns the corresponding value component{'fg':{'rgb':[255,255,255], 'intensity':1, 'bg':{'rgb':[0,0,0], 'intensity':1}, 'sp':{'fg':[0,0,255], 'intensity':0.5}}} |
nil |
The amount of tint that can be applied against each highlight component (fg, bg, sp). Intensity is a float value [0-1], where 1 is the most intense and 0 is not tinted. See the tinting tutorial for more details (TODO link). |
basebg |
'#FFFFFF' [255,255,255] 0xFFFFFF |
nil |
This value manipulates the target background color. This is most useful for transparent windows, where the Normal bg is NONE. Set this value to a good target value to improve fading accuracy. |
blocklist |
When set via lua or python, the top level named object can be a function(win)=>bool . Each nested object or value can also be a function(relative_config)=>bool . True indicates blocked, False not linked, nil indeterminate.{[key:string]: {'buf_opts': {[key]:string: value}, 'buf_vars': {...}, 'win_opts': {...}, 'win_vars': 'win_config': {...}}} |
{'default':{'buf_opts': {'buftype':['prompt', 'terminal', 'popup']}, 'win_config': {'relative': 1}}} |
If the window is determined to be blocked, Vimade highlights will be removed and it will skip the styling process. See the block and linking section for more details (TODO link). |
link |
When set via lua or python, the top level named object can be a function(win, active_win)=>bool . Each nested object or value can also be a function(relative_win_obj,active_win_obj)=>bool . True indicates linked, False not linked, nil indeterminate. |
nil |
Determines whether the current window should be linked and unhighlighted with the active window. groupdiff and groupscrollbind tie into the default behavior of this object behind the scenes to unlink diffs. See the block and linking section for more details (TODO link). |
groupdiff |
0 1 bool |
1 |
highlights and unhighlights diff windows together. |
groupscrollbind |
0 1 bool |
0 |
highlights and unhighlights scrolllbound windows together. |
checkinterval |
int |
100 -500 |
Time in milliseconds before re-checking windows. Default varies depending on Neovim, terminals, and gui vim. |
usecursorhold |
0 1 bool |
0 |
Whether to use cursorhold events instead of async timer. Setting this option disables the timer. This option defaults to 0 for most editor versions. gvim defaults to 1 due to async timers breaking visual selections. If you use this value, remember to set :set updatetime appropriately. |
enablefocusfading |
0 1 bool |
0 |
Highlight the active window on application focus and blur events. This can be desirable when switching applications, but requires additional setup for terminal and tmux. See enablefocusfading section for more details (TODO link) |
normalid |
int |
nil | The id of the Normal highlight. Vimade will automatically set this, so you don't need to worry about it. You can override it though if you just want to play around. |
normalncid |
int |
nil | The id of the NormalNC highlight. Vimade will automatically set this, so you don't need to worry about it. You can override it though if you just want to play around. |
lazy |
1 0 |
nil | When set to 1 Vimade is disabled at startup. You will need to manually call vimade#Load() . See lazy loading section for more details. |
Configuration options only for lua
option | values/type | default | description |
---|---|---|---|
nohlcheck |
bool |
true |
When set to false , Vimade will recompute namespaces each frame. This is useful if you have a plugin that dynamically changes highlights periodically. When to true Vimade only recomputes namespaces when you switch between buffers/windows. Performance isn't an issue either way as the recomputation process is sub-millisecond. |
Configuration options only for python
option | values/type | default | description |
---|---|---|---|
enablesigns |
0 1 bool |
True |
Whether or not to fade signs. For python this has to be performed per-buffer. If you want per-window signs, you will need to link your sign highlights to Normal. |
signsid |
int |
13100 |
The id that should be used to generate sign. This is required to avoid collisions with other plugins. |
signsretentionperiod |
int |
4000 |
The amount of time after a window becomes inactive to check for sign updates. Many plugins asynchronously update the buffer after switching windows, this helps ensure signs stay faded. |
fademinimap |
0 1 bool |
1 |
Enables a special fade effect for severin-lemaignan/vim-minimap . Setting vimade.fademinimap to 0 disables the special fade. |
matchpriority |
int |
10 |
Controls the highlighting priority. You may want to tweak this value to make Vimade play nicely with other highlighting plugins and behaviors. For example, if you want hlsearch to show results on all buffers, you may want to lower this value to 0. |
linkwincolor |
string[] |
[] |
Vim only option when wincolor is supported. List of highlights that will be linked to Normal . Normal is highlighted using setlocal wincolor , which gives Vim some flexibility to target highlight groups (see minimalist recipe). |
disablebatch |
0 1 bool |
0 |
Disables IPC batching. Enabling this will greatly reduce performance, but allow you debug issues. |
enablebasegroups |
0 1 bool |
true |
Only old Neovim. Allows winlocal winhl for the basegroups listed below. |
basegroups |
string[] |
every built-in highlight | Only old Neovim. Fades the listed highlights in addition to the buffer text. |
enabletreesitter |
0 1 bool |
0 |
Only old Neovim. Uses treesitter to directly query highlight groups instead of relying on synID . |
Commands
command | description |
---|---|
VimadeEnable |
Enables Vimade. Not necessary to run unless you have explicitly disabled Vimade. |
VimadeDisable |
Disable and remove all Vimade highlights. |
VimadeToggle |
Toggle between enabled/disabled states. |
VimadeRedraw |
Force vimade to recalculate and redraw every highlight. |
VimadeInfo |
Provides debug information for Vimade. Please include this info in bug reports. |
VimadeWinDisable |
Disables fading for the current window. |
VimadeWinEnable |
Enables fading for the current window. |
VimadeBufDisable |
Disables fading for the current buffer. |
VimadeBufEnable |
Enables fading for the current buffer. |
VimadeFadeActive |
Fades the current active window. |
VimadeUnfadeActive |
Unfades the current active window. |
VimadeOverrideFolded |
Overrides the Folded highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include Folded highlights that are distracting in faded windows. |
VimadeOverrideSignColumn |
Overrides the SignColumn highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include SignColumn highlights that are distracting in faded windows. |
VimadeOverrideLineNr |
Overrides the LineNr highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include LineNr highlights that are distracting in faded windows. |
VimadeOverrideSplits |
Overrides the VertSplit highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include VertSplit highlights that are distracting in faded windows. |
VimadeOverrideNonText |
Overrides the NonText highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include NonText highlights that are distracting in faded windows. |
VimadeOverrideEndOfBuffer |
Overrides the EndOfBuffer highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include EndOfBuffer highlights that are distracting in faded windows. |
VimadeOverrideAll |
Combines all VimadeOverride commands. |
VimadeFadeLevel [0.0-1.0] |
Sets the FadeLevel config and forces an immediate redraw. |
VimadeFadePriority [0+] |
Sets the FadePriority config and forces an immediate redraw. |
FAQ/Help
Tmux is not working!
- Vimade only works in a 256 or higher color mode and by default TMUX may set t_Co to 8. it is recommended that you set
export TERM=xterm-256color
before starting vim. You can also setset termguicolors
inside vim if your term supports it for an even more accurate level of fading.
Many similar Neovim plugins have recently been created. I'm not aware of any feature that these plugins support that Vimade doesn't, so I'm going to keep this table limited to key differences. If you find a feature gap, please file an issue or contribute!
Feature | Vimade | Shade | Tint | Sunglasses |
---|---|---|---|---|
Fade buffers | Yes | |||
Fade windows | Yes | Yes | Yes | Yes |
Group diffs | Yes | via function | ||
Links | Yes | via function | ||
Blocklist | Yes | via function | Yes | |
Animations | Yes | |||
Recipes | Yes | |||
256 colors | Yes | |||
Per-window config (e.g. fadelevel, tint, etc) | Yes | |||
Cleared highlights | Yes | Yes | ||
Supports Vim + All versions of Neovim | Yes |
Feel free to open a PR or file issues for bugs and feature requests. All contributions are valued even its just a question! If you are looking for a place to share your own code and flavor in this plugin, recipes are a great starting place.
Thanks for reading!