├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── autoload └── vimade.vim ├── colors.sh ├── doc └── vimade.txt ├── lib ├── __init__.py └── vimade │ ├── __init__.py │ ├── animator.py │ ├── bridge.py │ ├── config_helpers │ ├── __init__.py │ ├── blocklist.py │ └── link.py │ ├── fader.py │ ├── highlighter.py │ ├── recipe │ ├── __init__.py │ ├── default.py │ ├── duo.py │ ├── minimalist.py │ └── ripple.py │ ├── signs.py │ ├── state │ ├── __init__.py │ ├── globals.py │ ├── namespace.py │ └── win.py │ ├── style │ ├── __init__.py │ ├── exclude.py │ ├── fade.py │ ├── include.py │ ├── invert.py │ ├── tint.py │ └── value │ │ ├── __init__.py │ │ ├── animate.py │ │ ├── condition.py │ │ ├── direction.py │ │ └── ease.py │ └── util │ ├── __init__.py │ ├── color.py │ ├── ipc.py │ ├── matchers.py │ ├── promise.py │ ├── type.py │ └── validate.py ├── lua ├── vimade │ ├── animator.lua │ ├── config_helpers │ │ ├── blocklist.lua │ │ └── link.lua │ ├── fader.lua │ ├── focus │ │ ├── api.lua │ │ ├── commands.lua │ │ ├── core.lua │ │ ├── defaults.lua │ │ ├── init.lua │ │ └── providers │ │ │ ├── blanks.lua │ │ │ ├── hlchunk.lua │ │ │ ├── mini.lua │ │ │ ├── snacks.lua │ │ │ ├── static.lua │ │ │ └── treesitter.lua │ ├── highlighters │ │ ├── namespace.lua │ │ └── terminal.lua │ ├── init.lua │ ├── recipe │ │ ├── default.lua │ │ ├── duo.lua │ │ ├── minimalist.lua │ │ ├── paper.lua │ │ ├── paradox.lua │ │ ├── ripple.lua │ │ └── space.lua │ ├── state │ │ ├── globals.lua │ │ ├── namespace.lua │ │ ├── real_namespace.lua │ │ └── win.lua │ ├── style │ │ ├── component.lua │ │ ├── exclude.lua │ │ ├── fade.lua │ │ ├── include.lua │ │ ├── invert.lua │ │ ├── link.lua │ │ ├── tint.lua │ │ └── value │ │ │ ├── animate.lua │ │ │ ├── condition.lua │ │ │ ├── direction.lua │ │ │ └── ease.lua │ └── util │ │ ├── color.lua │ │ ├── compat.lua │ │ ├── events.lua │ │ ├── key_reducer.lua │ │ ├── matchers.lua │ │ ├── type.lua │ │ └── validate.lua └── vimade_legacy_treesitter.lua ├── plugin └── vimade.vim └── vimade.vimdoc.vim /.gitattributes: -------------------------------------------------------------------------------- 1 | lib/vimade/state/*.* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | /doc/tags 4 | /init.vim 5 | tags 6 | -------------------------------------------------------------------------------- /colors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | col=$1 4 | proc=$$ 5 | if [ "$2" = "tmux" ] || [ "$3" = "tmux" ] 6 | then 7 | printf "\033Ptmux;\033\033]$col;?\007\033\\" >> /dev/tty 8 | else 9 | if [ "$2" = "7" ] || [ "$3" == "7" ] 10 | then 11 | printf "\033]$col;?\007" >> /dev/tty 12 | else 13 | printf "\033]$col;?\033\\" >> /dev/tty 14 | fi 15 | fi 16 | (sleep 0.05; kill $proc >> /dev/null;)& 17 | child1=$! 18 | read -r -t 1 -d $'\x1b' 19 | if [ "$2" = "7" ] || [ "$3" == "7" ] 20 | then 21 | read -r -t 1 -d $'\007' color 22 | else 23 | read -r -t 1 -d $'\\' color 24 | fi 25 | kill $child1 2>/dev/null 26 | wait $child1 2>/dev/null 27 | echo $color 28 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/__init__.py -------------------------------------------------------------------------------- /lib/vimade/__init__.py: -------------------------------------------------------------------------------- 1 | from vimade import bridge 2 | vimade = bridge 3 | -------------------------------------------------------------------------------- /lib/vimade/animator.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.util import ipc as IPC 5 | 6 | FADER = None 7 | GLOBALS = None 8 | 9 | M.scheduled = False 10 | M.animating = False 11 | M.queued_windows = {} 12 | 13 | def __init(args): 14 | global FADER 15 | global GLOBALS 16 | FADER = args['FADER'] 17 | GLOBALS = args['GLOBALS'] 18 | FADER.on('tick:before', _tick_before) 19 | FADER.on('tick:after', _tick_after) 20 | 21 | def _tick_before(): 22 | if M.scheduled == True: 23 | M.scheduled = False 24 | M.animating = True 25 | 26 | def _tick_after(): 27 | if M.animating == True: 28 | M.animating = False 29 | if M.scheduled == True: 30 | IPC.eval_and_return('vimade#StartAnimationTimer()') 31 | 32 | def schedule(win): 33 | if not win.winid in queued_windows: 34 | queued_windows[win.winid] = True 35 | M.scheduled = True 36 | 37 | def refresh(): 38 | only_these_windows = M.queued_windows 39 | M.queued_windows = {} 40 | return only_these_windows 41 | -------------------------------------------------------------------------------- /lib/vimade/bridge.py: -------------------------------------------------------------------------------- 1 | from vimade import fader as FADER 2 | 3 | def setup(**kwargs): 4 | return FADER.setup(**kwargs) 5 | 6 | def getInfo(): 7 | return FADER.getInfo() 8 | 9 | def disable(): 10 | FADER.disable() 11 | 12 | def invalidate(): 13 | # distinction between buffer and signs invalidation is not needed, this basically just mean 14 | # recheck the screen. 15 | FADER.invalidate() 16 | 17 | def recalculate(): 18 | FADER.recalculate() 19 | 20 | def update(): 21 | FADER.tick() 22 | 23 | def animate(): 24 | FADER.animate() 25 | -------------------------------------------------------------------------------- /lib/vimade/config_helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/config_helpers/__init__.py -------------------------------------------------------------------------------- /lib/vimade/config_helpers/blocklist.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.util import matchers as MATCHERS 5 | from vimade.state import globals as GLOBALS 6 | 7 | _minimap_matcher = MATCHERS.StringMatcher('-minimap') 8 | 9 | def DEFAULT(win, active, config): 10 | legacy = None 11 | if GLOBALS.fademinimap == False or 'buf_name' in config: 12 | legacy = (_minimap_matcher(win.buf_name) if GLOBALS.fademinimap == False else False) 13 | return legacy \ 14 | or (MATCHERS.ContainsString(config['buf_name'])(win.buf_name) if config.get('buf_name') else False) \ 15 | or (MATCHERS.ContainsString(config['win_type'])(win.win_type) if config.get('win_type') else False) \ 16 | or (MATCHERS.ContainsAny(config['buf_opts'])(win.buf_opts) if config.get('buf_opts') else False) \ 17 | or (MATCHERS.ContainsAny(config['buf_vars'])(win.buf_vars) if config.get('buf_vars') else False) \ 18 | or (MATCHERS.ContainsAny(config['win_opts'])(win.win_opts) if config.get('win_opts') else False) \ 19 | or (MATCHERS.ContainsAny(config['win_vars'])(win.win_vars) if config.get('win_vars') else False) \ 20 | or (MATCHERS.ContainsAny(config['win_config'])(win.win_config) if config.get('win_config') else False) 21 | -------------------------------------------------------------------------------- /lib/vimade/config_helpers/link.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.util import matchers as MATCHERS 5 | from vimade.state import globals as GLOBALS 6 | 7 | def DEFAULT(win, active, config): 8 | legacy = False 9 | if GLOBALS.groupdiff == True or GLOBALS.groupscrollbind == True: 10 | win_wo = win.win_opts 11 | active_wo = active.win_opts 12 | legacy = (GLOBALS.groupdiff and MATCHERS.EachContainsAny({'diff': True})([active_wo, win_wo])) \ 13 | or (GLOBALS.groupscrollbind and MATCHERS.EachContainsAny({'scrollbind': True})([active_wo, win_wo])) 14 | return legacy \ 15 | or (MATCHERS.ContainsString(config['buf_name'])(win.buf_name) and MATCHERS.ContainsString(config.buf_name)(active.buf_name) if config.get('buf_name') else False) \ 16 | or (MATCHERS.ContainsString(config['win_type'])(win.win_type) and MATCHERS.ContainsString(config.win_type)(active.win_type) if config.get('win_type') else False) \ 17 | or (MATCHERS.EachContainsAny(config['buf_opts'])([active.buf_opts, win.buf_opts]) if config.get('buf_opts') else False) \ 18 | or (MATCHERS.EachContainsAny(config['buf_vars'])([active.buf_vars, win.buf_vars]) if config.get('buf_vars') else False) \ 19 | or (MATCHERS.EachContainsAny(config['win_opts'])([active.win_opts, win.win_opts]) if config.get('win_opts') else False) \ 20 | or (MATCHERS.EachContainsAny(config['win_vars'])([active.win_vars, win.win_vars]) if config.get('win_vars') else False) \ 21 | or (MATCHERS.EachContainsAny(config['win_config'])([active.win_config, win.win_config]) if config.get('win_config') else False) 22 | -------------------------------------------------------------------------------- /lib/vimade/fader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import vim 4 | M = sys.modules[__name__] 5 | 6 | from vimade.util.promise import Promise, all 7 | from vimade.style.value import animate as ANIMATE 8 | from vimade import animator as ANIMATOR 9 | from vimade.style import exclude as EXCLUDE 10 | from vimade.style import fade as FADE 11 | from vimade.style import tint as TINT 12 | from vimade.style import include as INCLUDE 13 | from vimade import highlighter as HIGHLIGHTER 14 | from vimade import signs as SIGNS 15 | from vimade.state import globals as GLOBALS 16 | from vimade.state import win as WIN_STATE 17 | from vimade.state import namespace as NAMESPACE 18 | from vimade.util import ipc as IPC 19 | 20 | def _pairs(input): 21 | if type(input) == list: 22 | return enumerate(input) 23 | elif type(input) == dict: 24 | return input.items() 25 | 26 | def _return_to_win(): 27 | current_winid = int(IPC.eval_and_return('win_getid()')) 28 | start_winid = GLOBALS.current['winid'] 29 | if current_winid != start_winid: 30 | vim.command('noautocmd call win_gotoid(%d)' % (start_winid)) 31 | 32 | def _update(only_these_windows): 33 | promise = Promise() 34 | # start_time = time.time() 35 | windows = IPC.eval_and_return('getwininfo()') 36 | current = GLOBALS.current 37 | current_promise = None 38 | other_promises = [] 39 | updated_cache = {} 40 | 41 | # if highlights are invalidated at the global level, we need to 42 | if (GLOBALS.RECALCULATE & GLOBALS.tick_state) > 0: 43 | HIGHLIGHTER.clear_base_cache() 44 | unhighlightAll(windows) 45 | # This redraw is needed for basegroups in certain versions of Neovim 46 | # Neovim async API breaks in certain scenarios and doesn't update the backing color mechanism 47 | if GLOBALS.enablebasegroups: 48 | vim.command('redraw!') 49 | 50 | style = GLOBALS.style 51 | for s in style: 52 | if s.tick: 53 | s.tick() 54 | 55 | HIGHLIGHTER.refresh_vimade_0() 56 | 57 | for wininfo in windows: 58 | # we skip only_these_windows here because we need to know who the active window is 59 | # for linking and other ops 60 | # python is a bit tricker because the diff window hl overrides the default 61 | # we get around this refreshing the active, but not determining its end state 62 | # we then complete the end state after all other windows have been refreshed 63 | if current['winid'] == int(wininfo['winid']): 64 | current_promise = WIN_STATE.refresh_active(wininfo) 65 | break 66 | 67 | for wininfo in windows: 68 | if current['tabnr'] == int(wininfo['tabnr']) and current['winid'] != int(wininfo['winid']) and \ 69 | (not only_these_windows or only_these_windows.get(int(wininfo['winid']))): 70 | other_promises.append(WIN_STATE.refresh(wininfo)) 71 | 72 | def finish(not_current_win): 73 | for win in not_current_win: 74 | win.finish() 75 | current_promise.then(lambda win: win.finish()) 76 | all(other_promises).then(finish) 77 | 78 | def next(val): 79 | def complete(val): 80 | promise.resolve(None) 81 | # delta = time.time() - start_time 82 | # if delta * 1000 > 3: 83 | # print('d', delta*1000) 84 | pass 85 | WIN_STATE.cleanup(windows) 86 | _return_to_win() 87 | # start_time = time.time() 88 | SIGNS.flush().then(complete) 89 | 90 | IPC.flush_batch().then(next) 91 | return promise 92 | 93 | def _after_promise(val): 94 | _return_to_win() 95 | 96 | _callbacks = {} 97 | def on(name, callback): 98 | callbacks = _callbacks.get(name) 99 | if not callbacks: 100 | _callbacks[name] = callbacks = [] 101 | callbacks.append(callback) 102 | 103 | def notify(name): 104 | callbacks = _callbacks.get(name) 105 | if callbacks: 106 | for callback in callbacks: 107 | callback() 108 | 109 | def setup(**kwargs): 110 | return GLOBALS.setup(**kwargs) 111 | 112 | def getInfo(): 113 | return GLOBALS.getInfo() 114 | 115 | def recalculate(): 116 | tick(GLOBALS.RECALCULATE | GLOBALS.INVALIDATE_HIGHLIGHTS | GLOBALS.CHANGED) 117 | 118 | def invalidate(): 119 | tick(GLOBALS.CHANGED) 120 | 121 | def tick(tick_state = GLOBALS.READY, only_these_windows = None): 122 | last_ei = vim.options['ei'] 123 | vim.options['ei'] = 'all' 124 | notify('tick:before') 125 | GLOBALS.refresh(tick_state) 126 | 127 | # if the tick_state changed during an animation, we need to use that frame 128 | # to sync the windows 129 | if GLOBALS.tick_state > 0 and only_these_windows: 130 | only_these_windows = None 131 | 132 | def after_update(v): 133 | notify('tick:after') 134 | _update(only_these_windows).then(after_update) 135 | 136 | 137 | vim.options['ei'] = last_ei 138 | 139 | def animate(): 140 | only_these_windows = ANIMATOR.refresh() 141 | tick(GLOBALS.READY, only_these_windows) 142 | 143 | def disable(): 144 | unhighlightAll() 145 | 146 | def unhighlightAll(windows = None): 147 | if windows == None: 148 | windows = IPC.eval_and_return('getwininfo()') 149 | for wininfo in windows: 150 | WIN_STATE.unhighlight(int(wininfo['winid'])) 151 | SIGNS.flush() 152 | 153 | M.ANIMATE.__init({'FADER': M, 'GLOBALS': GLOBALS}) 154 | M.ANIMATOR.__init({'FADER': M, 'GLOBALS': GLOBALS}) 155 | M.FADE.__init({'FADER': M, 'GLOBALS': GLOBALS}) 156 | M.TINT.__init({'FADER': M, 'GLOBALS': GLOBALS}) 157 | M.EXCLUDE.__init({'FADER': M, 'GLOBALS': GLOBALS}) 158 | M.INCLUDE.__init({'FADER': M, 'GLOBALS': GLOBALS}) 159 | M.IPC.__init({'FADER': M, 'GLOBALS': GLOBALS}) 160 | M.WIN_STATE.__init({'FADER': M, 'GLOBALS': GLOBALS}) 161 | M.HIGHLIGHTER.__init({'FADER': M, 'GLOBALS': GLOBALS, 'WIN': WIN_STATE, 'NAMESPACE': NAMESPACE}) 162 | -------------------------------------------------------------------------------- /lib/vimade/recipe/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/recipe/__init__.py -------------------------------------------------------------------------------- /lib/vimade/recipe/default.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.style.value import animate as ANIMATE 5 | from vimade.style.value import condition as CONDITION 6 | from vimade.style import fade as FADE 7 | from vimade.style import tint as TINT 8 | from vimade.util import type as TYPE 9 | 10 | def animate_default(**kwargs): 11 | condition = kwargs.get('condition') 12 | animation = { 13 | 'duration': kwargs.get('duration'), 14 | 'delay': kwargs.get('delay'), 15 | 'ease': kwargs.get('ease'), 16 | 'direction': kwargs.get('direction'), 17 | } 18 | return [ 19 | TINT.Tint( 20 | condition = condition, 21 | value = ANIMATE.Tint(**TYPE.extend({}, animation, { 22 | 'to': TINT.Default().value() 23 | })) 24 | ), 25 | FADE.Fade( 26 | condition = condition, 27 | value = ANIMATE.Number(**TYPE.extend({}, animation, { 28 | 'to': FADE.Default().value(), 29 | 'start': 1, 30 | })) 31 | )] 32 | 33 | def default(**kwargs): 34 | return [TINT.Default(**kwargs), FADE.Default(**kwargs)] 35 | 36 | # @param **kwargs { 37 | # @optional animate: boolean = false 38 | # @optional ease: EASE = ANIMATE.DEFAULT_EASE 39 | # @optional delay: number = ANIMATE.DEFAULT_DELAY 40 | # @optional duration: number = ANIMATE.DEFAULT_DURATION 41 | # @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 42 | # } 43 | def Default(**kwargs): 44 | return { 45 | 'style': animate_default(**kwargs) if kwargs.get('animate') else default(**kwargs), 46 | } 47 | -------------------------------------------------------------------------------- /lib/vimade/recipe/duo.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import vim 3 | M = sys.modules[__name__] 4 | 5 | from vimade.state import globals as GLOBALS 6 | from vimade.style.value import animate as ANIMATE 7 | from vimade.style.value import direction as DIRECTION 8 | from vimade.style import fade as FADE 9 | from vimade.style import tint as TINT 10 | from vimade.util import type as TYPE 11 | 12 | def _duo_tint_to(config): 13 | buffer_pct = config.get('buffer_pct') 14 | window_pct = config.get('window_pct') 15 | def _duo_tint_result(style, state): 16 | to = style.resolve(TINT.Default().value(), state) 17 | if to and style.win.tabnr == GLOBALS.current['tabnr']: 18 | pct = window_pct 19 | if not style.win.nc: 20 | pct = 0 21 | elif style.win.bufnr == GLOBALS.current['bufnr']: 22 | pct = buffer_pct 23 | for color in to.values(): 24 | if 'rgb' in color: 25 | if color.get('intensity') == None: 26 | color['intensity'] = 1 27 | color['intensity'] = color['intensity'] * pct 28 | return to 29 | return _duo_tint_result 30 | def _duo_fade_to(config): 31 | buffer_pct = config.get('buffer_pct') 32 | window_pct = config.get('window_pct') 33 | def _duo_fade_result(style, state): 34 | if not style.win.nc: 35 | return 1 36 | to = style.resolve(FADE.Default().value(), state) 37 | pct = window_pct 38 | if to != None and style.win.tabnr == GLOBALS.current['tabnr'] and style.win.bufnr == GLOBALS.current['bufnr']: 39 | pct = buffer_pct 40 | return to + (1 - to) * (1 - pct) 41 | return _duo_fade_result 42 | def animate_duo(config): 43 | condition = config.get('condition') 44 | animation = { 45 | 'duration': config.get('duration'), 46 | 'delay': config.get('delay'), 47 | 'ease': config.get('ease'), 48 | 'direction': config.get('direction'), 49 | } 50 | return [ 51 | TINT.Tint( 52 | condition = condition, 53 | value = ANIMATE.Tint(**TYPE.extend({}, animation, { 54 | 'to': _duo_tint_to(config), 55 | }) 56 | )), 57 | FADE.Fade( 58 | condition = condition, 59 | value = ANIMATE.Number(**TYPE.extend({}, animation, { 60 | 'start': 1, 61 | 'to': _duo_fade_to(config), 62 | }) 63 | )), 64 | ] 65 | 66 | def duo(config): 67 | return [ 68 | TINT.Default( 69 | condition = config.get('condition'), 70 | value = _duo_tint_to(config), 71 | ), 72 | FADE.Default( 73 | condition = config.get('condition'), 74 | value = _duo_fade_to(config), 75 | ), 76 | ] 77 | 78 | # @param **kwargs { 79 | # @optional buffer_pct: number[0-1] = 0.382 80 | # @optional window_pct: number[0-1] = 1 81 | # @optional animate: boolean = false 82 | # @optional ease: EASE = ANIMATE.DEFAULT_EASE 83 | # @optional delay: number = ANIMATE.DEFAULT_DELAY 84 | # @optional duration: number = ANIMATE.DEFAULT_DURATION 85 | # @optional direction: DIRECTION = DIRECTION.IN_OUT 86 | # @optional ncmode: 'windows'|'buffers' = 'windows' 87 | # } 88 | def Duo(**kwargs): 89 | config = TYPE.shallow_copy(kwargs) 90 | config['ncmode'] = config['ncmode'] if config.get('ncmode') != None else 'windows' 91 | config['buffer_pct'] = config['buffer_pct'] if config.get('buffer_pct') != None else 0.382 92 | config['window_pct'] = config['window_pct'] if config.get('window_pct') != None else 1 93 | config['direction'] = config['direction'] if config.get('direction') != None else DIRECTION.IN_OUT 94 | return { 95 | 'style': animate_duo(config) if config.get('animate') else duo(config), 96 | 'ncmode': config['ncmode'], 97 | } 98 | -------------------------------------------------------------------------------- /lib/vimade/recipe/minimalist.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import vim 3 | M = sys.modules[__name__] 4 | 5 | from vimade.state import globals as GLOBALS 6 | from vimade.style.value import animate as ANIMATE 7 | from vimade.style.value import condition as CONDITION 8 | from vimade.style import fade as FADE 9 | from vimade.style import tint as TINT 10 | from vimade.style.exclude import Exclude 11 | from vimade.style.include import Include 12 | from vimade.util import type as TYPE 13 | 14 | EXCLUDE_NAMES = ['LineNr', 'LineNrBelow', 'LineNrAbove', 'WinSeparator', 'EndOfBuffer', 'NonText', 'VimadeWC'] 15 | NO_VISIBILITY_NAMES = ['LineNr', 'LineNrBelow', 'LineNrAbove', 'EndOfBuffer', 'NonText', 'VimadeWC'] 16 | LOW_VISIBILITY_NAMES = ['WinSeparator'] 17 | 18 | def animate_minimalist(config): 19 | condition = config.get('condition') 20 | animation = { 21 | 'duration': config.get('duration'), 22 | 'delay': config.get('delay'), 23 | 'ease': config.get('ease'), 24 | 'direction': config.get('direction'), 25 | } 26 | return [ 27 | TINT.Tint( 28 | condition = condition, 29 | value = ANIMATE.Tint(**TYPE.extend({}, animation, { 30 | 'to': TINT.Default().value() 31 | })) 32 | ), 33 | Exclude( 34 | condition = condition, 35 | value = config.get('exclude_names'), 36 | style = [ 37 | FADE.Fade( 38 | value = ANIMATE.Number(**TYPE.extend({}, animation, { 39 | 'to': FADE.Default().value(), 40 | 'start': 1 41 | })) 42 | ) 43 | ] 44 | ), 45 | Include( 46 | condition = condition, 47 | value = config.get('no_visibility_names'), 48 | style = [ 49 | FADE.Fade( 50 | value = ANIMATE.Number(**TYPE.extend({}, animation, { 51 | 'to': 0, 52 | 'start': 1 53 | })) 54 | ) 55 | ] 56 | ), 57 | Include( 58 | condition = condition, 59 | value = config.get('low_visibility_names'), 60 | style = [ 61 | FADE.Fade( 62 | value = ANIMATE.Number(**TYPE.extend({}, animation, { 63 | 'to': config.get('low_visibility_fadelevel'), 64 | 'start': 1 65 | })) 66 | ) 67 | ] 68 | ), 69 | ] 70 | 71 | def minimalist(config): 72 | condition = config.get('condition') 73 | return [ 74 | TINT.Default(**config), 75 | Exclude( 76 | condition = condition, 77 | value = config.get('exclude_names'), 78 | style = [FADE.Default()] 79 | ), 80 | Include( 81 | condition = condition, 82 | value = config.get('no_visibility_names'), 83 | style = [FADE.Fade(value = 0)] 84 | ), 85 | Include( 86 | condition = condition, 87 | value = config.get('low_visibility_names'), 88 | style = [FADE.Fade(value = config.get('low_visibility_fadelevel'))] 89 | ), 90 | ] 91 | 92 | # @param **kwargs { 93 | # @optional animate: boolean = false 94 | # @optional ease: EASE = ANIMATE.DEFAULT_EASE 95 | # @optional delay: number = ANIMATE.DEFAULT_DELAY 96 | # @optional duration: number = ANIMATE.DEFAULT_DURATION 97 | # @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 98 | # } 99 | def Minimalist(**kwargs): 100 | config = TYPE.shallow_copy(kwargs) 101 | config['exclude_names'] = config['exclude_names'] if config.get('exclude_names') != None else EXCLUDE_NAMES 102 | config['no_visibility_names'] = config['no_visibility_names'] if config.get('no_visibility_names') != None else NO_VISIBILITY_NAMES 103 | config['low_visibility_names'] = config['low_visibility_names'] if config.get('low_visibility_names') != None else LOW_VISIBILITY_NAMES 104 | config['low_visibility_fadelevel'] = config['low_visibility_fadelevel'] if config.get('low_visibility_fadelevel') != None else 0.2 105 | return { 106 | 'style': animate_minimalist(config) if config.get('animate') else minimalist(config), 107 | 'linkwincolor': [] if GLOBALS.is_nvim else [x for x in config['no_visibility_names'] if x != 'VimadeWC'], 108 | } 109 | -------------------------------------------------------------------------------- /lib/vimade/recipe/ripple.py: -------------------------------------------------------------------------------- 1 | import vim 2 | import sys 3 | import math 4 | M = sys.modules[__name__] 5 | 6 | from vimade.style.value import animate as ANIMATE 7 | from vimade.style.value import condition as CONDITION 8 | from vimade.style.value import direction as DIRECTION 9 | from vimade.style.value import ease as EASE 10 | from vimade.state import globals as GLOBALS 11 | from vimade.style import fade as FADE 12 | from vimade.style import tint as TINT 13 | from vimade.util import type as TYPE 14 | from vimade.util import ipc as IPC 15 | 16 | def _get_win_infos(): 17 | def distance(a1, a2, b1, b2): 18 | return math.sqrt(math.pow(a1-b1, 2) + math.pow(a2-b2, 2)) 19 | def distance_between(info_a, info_b): 20 | left_a = float(info_a['wincol']) * 0.75 21 | left_b = float(info_b['wincol']) * 0.75 22 | right_a = left_a + float(info_a['width']) * 0.75 23 | right_b = left_b + float(info_b['width']) * 0.75 24 | top_a = info_a['winrow'] 25 | top_b = info_b['winrow'] 26 | bottom_a = top_a + info_a['height'] 27 | bottom_b = top_b + info_b['height'] 28 | if (bottom_a < top_b) and (right_b < left_a): 29 | return distance(left_a, bottom_a, right_b, top_b) 30 | elif (right_b < left_a) and (bottom_b < top_a): 31 | return distance(left_a, top_a, right_b, bottom_b) 32 | elif (bottom_b < top_a) and (right_a < left_b): 33 | return distance(right_a, top_a, left_b, bottom_b) 34 | elif (right_a < left_b) and (bottom_a < top_b): 35 | return distance(right_a, bottom_a, left_b, top_b) 36 | elif (right_b < left_a): 37 | return left_a - right_b 38 | elif (right_a < left_b): 39 | return left_b - right_a 40 | elif (bottom_b < top_a): 41 | return top_a - bottom_b 42 | elif (bottom_a < top_b): 43 | return top_b - bottom_a 44 | else: 45 | return 0 46 | wininfo = IPC.eval_and_return('getwininfo()') 47 | current_win = GLOBALS.current['winid'] 48 | found_cur = None 49 | for info in wininfo: 50 | if int(info['winid']) == current_win: 51 | found_cur = info 52 | info['wincol'] = int(info['wincol']) 53 | info['winrow'] = int(info['winrow']) 54 | info['width'] = int(info['width']) 55 | info['height'] = int(info['height']) 56 | break 57 | result = {} 58 | for info in wininfo: 59 | if GLOBALS.current['tabnr'] == int(info['tabnr']): 60 | info['wincol'] = int(info['wincol']) 61 | info['winrow'] = int(info['winrow']) 62 | info['width'] = int(info['width']) 63 | info['height'] = int(info['height']) 64 | result[int(info['winid'])] = { 65 | 'dist': distance_between(info, found_cur), 66 | 'area': info['width'] * info['height'], 67 | } 68 | return result 69 | 70 | M._win_infos = None 71 | M._max_distance = None 72 | M._max_area = None 73 | def _ripple_tick(): 74 | M._win_infos = _get_win_infos() 75 | M._max_distance = 0 76 | M._max_area = 0 77 | for winid, info in M._win_infos.items(): 78 | M._max_distance = max(info['dist'], M._max_distance) 79 | M._max_area = max(info['area'], M._max_area) 80 | def _ripple_to_tint(style, state): 81 | to = style.resolve(TINT.Default().value(), state) 82 | if not style.win.winid in M._win_infos: 83 | return to 84 | if to: 85 | for color in to.values(): 86 | if color.get('rgb'): 87 | if color.get('intensity') == None: 88 | color['intensity'] = 1 89 | color['intensity'] = float(color['intensity']) 90 | color['intensity'] = (float(M._win_infos[style.win.winid]['dist']) / float(M._max_distance or 1)) * color['intensity'] 91 | return to 92 | def _ripple_to_fade(style, state): 93 | to = style.resolve(FADE.Default().value(), state) 94 | if not style.win.winid in M._win_infos: 95 | return to 96 | to = float(to) 97 | return to + (1 - float(M._win_infos[style.win.winid]['dist']) / float(M._max_distance or 1)) * ((1-to) * 0.5) 98 | def _ripple_start_tint(style, state): 99 | start = style.resolve(TINT.Default().value(), state) 100 | if start: 101 | for color in start.values(): 102 | color['intensity'] = 0 103 | return start 104 | def _ripple_duration(style, state): 105 | if M._max_distance == 0 or style.win.winid == -1: 106 | return 0 107 | return 100 + float(M._win_infos[style.win.winid]['dist']) / float(M._max_distance) * 200 108 | def _ripple_delay(style, state): 109 | if M._max_distance == 0 or style.win.winid == -1: 110 | return 0 111 | return float(M._win_infos[style.win.winid]['dist']) / float(M._max_distance) * 300 112 | 113 | def animate_ripple(config): 114 | return [ 115 | TINT.Tint( 116 | tick = _ripple_tick, 117 | condition = config.get('condition'), 118 | value = ANIMATE.Tint( 119 | to = _ripple_to_tint, 120 | start = _ripple_start_tint, 121 | delay = config.get('delay'), 122 | direction = config.get('direction'), 123 | duration = config.get('duration'), 124 | ease = config.get('ease'), 125 | ) 126 | ), 127 | FADE.Fade( 128 | condition = config.get('condition'), 129 | value = ANIMATE.Number( 130 | to = _ripple_to_fade, 131 | start = 1, 132 | delay = config.get('delay'), 133 | direction = config.get('direction'), 134 | duration = config.get('duration'), 135 | ease = config.get('ease'), 136 | ), 137 | )] 138 | 139 | def ripple(config): 140 | return [ 141 | TINT.Tint( 142 | tick = _ripple_tick, 143 | condition = config.get('condition'), 144 | value = _ripple_to_tint, 145 | ), 146 | FADE.Fade( 147 | condition = config.get('condition'), 148 | value = _ripple_to_fade, 149 | ), 150 | ] 151 | 152 | #@param config { 153 | # @optional animate: boolean = false 154 | # @optional condition: CONDITION = CONDITION.INACTIVE 155 | # @optional delay: number = function_gradual_based_on_dist 156 | # @optional direction: DIRECTION = DIRECTION.IN_OUT 157 | # @optional duration: number = function_gradual_based_on_dist 158 | # @optional ease: EASE = EASE.LINEAR 159 | # @optional ncmode: 'windows'|'buffers' = 'windows' 160 | #} 161 | def Ripple(**kwargs): 162 | config = TYPE.shallow_copy(kwargs) 163 | config['direction'] = config['direction'] if config.get('direction') else DIRECTION.IN_OUT 164 | config['delay'] = config['delay'] if config.get('delay') else _ripple_delay 165 | config['duration'] = config['duration'] if config.get('duration') else _ripple_duration 166 | config['ease'] = config['ease'] if config.get('ease') else EASE.LINEAR 167 | config['ncmode'] = config['ncmode'] if config.get('ncmode') else 'windows' 168 | return { 169 | 'style': animate_ripple(config) if config.get('animate') else ripple(config), 170 | 'ncmode': config.get('ncmode') 171 | } 172 | -------------------------------------------------------------------------------- /lib/vimade/state/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/state/__init__.py -------------------------------------------------------------------------------- /lib/vimade/style/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/style/__init__.py -------------------------------------------------------------------------------- /lib/vimade/style/exclude.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.style.value import condition as CONDITION 5 | 6 | GLOBALS = None 7 | 8 | def __init(args): 9 | global GLOBALS 10 | GLOBALS = args['GLOBALS'] 11 | M.__init = __init 12 | 13 | 14 | M.exclude_names = {} 15 | M.exclude_id = 1 16 | # @param **kwargs { 17 | # value: ['Folded', 'VertSplit', 'Normal', ...], # list of names that should be skipped on the style array 18 | # style: [Fade(0.4)] # style to run on all names that aren't excluded 19 | # } 20 | class Exclude(): 21 | def __init__(self, **kwargs): 22 | _condition = kwargs.get('condition') 23 | _condition = _condition if _condition != None else CONDITION.INACTIVE 24 | _names = kwargs.get('value', []) 25 | self.tick = kwargs.get('tick') 26 | class __Exclude(): 27 | def __init__(self, win, state): 28 | self.win = win 29 | self._condition = _condition 30 | self.condition = None 31 | self.names = [] 32 | self.style = [s.attach(win, state) for s in kwargs.get('style', [])] 33 | self.exclude = {} 34 | self._animating = False 35 | def before(self, win, state): 36 | self.condition = _condition(self, state) if callable(_condition) else _condition 37 | if self.condition == False: 38 | return 39 | names = self.names = _names(self, state) if callable(_names) else _names 40 | self.exclude = exclude = {} 41 | if type(names) == str: 42 | names = [names] 43 | for name in names: 44 | name_id = M.exclude_names.get(name) 45 | if not name_id: 46 | M.exclude_names[name] = name_id = str(M.exclude_id) 47 | M.exclude_id += 1 48 | exclude[name] = name_id 49 | for s in self.style: 50 | s.before(win, state) 51 | def key(self, win, state): 52 | if self.condition == False: 53 | return '' 54 | style_key = ','.join([s.key(win, state) for j, s in enumerate(self.style)]) 55 | if len(style_key) == 0: 56 | return '' 57 | return 'E-' + ','.join(self.exclude.values()) + '(' \ 58 | + style_key + ')' 59 | def modify(self, hl, to_hl): 60 | if self.condition == False or (hl['name'] in self.exclude): 61 | return 62 | else: 63 | for s in self.style: 64 | s.modify(hl, to_hl) 65 | self.attach = __Exclude 66 | -------------------------------------------------------------------------------- /lib/vimade/style/fade.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | M = sys.modules[__name__] 4 | 5 | from vimade.util import color as COLOR_UTIL 6 | from vimade.style.value import condition as CONDITION 7 | from vimade.util import type as TYPE 8 | from vimade.util import validate as VALIDATE 9 | 10 | GLOBALS = None 11 | 12 | def __init(args): 13 | global GLOBALS 14 | GLOBALS = args['GLOBALS'] 15 | M.__init = __init 16 | 17 | class Fade(): 18 | def __init__(parent, **kwargs): 19 | _condition = kwargs.get('condition') 20 | _condition = _condition if _condition != None else CONDITION.INACTIVE 21 | parent._value = kwargs.get('value') 22 | parent.tick = kwargs.get('tick') 23 | class __Fade(): 24 | def __init__(self, win, state): 25 | self.win = win 26 | self._condition = _condition 27 | self.condition = None 28 | self.fade = parent._value 29 | self._animating = False 30 | def resolve(self, value, state): 31 | return VALIDATE.fade(TYPE.resolve_all_fn(value, self, state)) 32 | def before(self, win, state): 33 | self.fade = self.resolve(parent._value, state) 34 | self.condition = _condition(self, state) if callable(_condition) else _condition 35 | def key(self, win, state): 36 | if self.condition == False: 37 | return '' 38 | return 'F-' + str(self.fade) 39 | def modify(self, hl, to_hl): 40 | if self.condition == False: 41 | return 42 | fade = self.fade 43 | fg = hl['fg'] 44 | bg = hl['bg'] 45 | sp = hl['sp'] 46 | ctermfg = hl['ctermfg'] 47 | ctermbg = hl['ctermbg'] 48 | if fg != None: 49 | hl['fg'] = COLOR_UTIL.interpolate24b(fg, to_hl['bg'], fade) 50 | if bg != None: 51 | hl['bg'] = COLOR_UTIL.interpolate24b(bg, to_hl['bg'], fade) 52 | if sp != None: 53 | hl['sp'] = COLOR_UTIL.interpolate24b(sp, to_hl['bg'], fade) 54 | if ctermfg != None: 55 | hl['ctermfg'] = COLOR_UTIL.interpolate256(ctermfg, to_hl['ctermbg'], fade) 56 | if ctermbg != None: 57 | hl['ctermbg'] = COLOR_UTIL.interpolate256(ctermbg, to_hl['ctermbg'], fade) 58 | return 59 | parent.attach = __Fade 60 | def value(parent, replacement = None): 61 | if replacement != None: 62 | parent._value = replacement 63 | return parent 64 | return parent._value 65 | 66 | def Default(**kwargs): 67 | return Fade(**TYPE.extend({ 68 | 'condition': CONDITION.INACTIVE, 69 | 'value': lambda style, state: GLOBALS.fadelevel(style, state) if callable(GLOBALS.fadelevel) else GLOBALS.fadelevel, 70 | }, kwargs)) 71 | -------------------------------------------------------------------------------- /lib/vimade/style/include.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.style.value import condition as CONDITION 5 | 6 | GLOBALS = None 7 | 8 | def __init(args): 9 | global GLOBALS 10 | GLOBALS = args['GLOBALS'] 11 | M.__init = __init 12 | 13 | 14 | M.include_names = {} 15 | M.include_id = 1 16 | # @param config = { 17 | # 'value': ['Folded', 'VertSplit', 'Normal', ...], # list of names that should be skipped on the style array 18 | # 'style': [Fade(0.4)] # style to run on all names that are include 19 | # } 20 | class Include(): 21 | def __init__(self, **kwargs): 22 | _condition = kwargs.get('condition') 23 | _condition = _condition if _condition != None else CONDITION.INACTIVE 24 | _names = kwargs.get('value', []) 25 | self.tick = kwargs.get('tick') 26 | class __Include(): 27 | def __init__(self, win, state): 28 | self.win = win 29 | self.condition = None 30 | self._condition = _condition 31 | self.names = [] 32 | self.style = [s.attach(win, state) for s in kwargs.get('style', [])] 33 | self.include = {} 34 | self._animating = False 35 | def before(self, win, state): 36 | self.condition = _condition(self, state) if callable(_condition) else _condition 37 | if self.condition == False: 38 | return 39 | names = self.names = _names(self, state) if callable(_names) else _names 40 | self.include = include = {} 41 | if type(names) == str: 42 | names = [names] 43 | for name in names: 44 | name_id = M.include_names.get(name) 45 | if not name_id: 46 | M.include_names[name] = name_id = str(M.include_id) 47 | M.include_id += 1 48 | include[name] = name_id 49 | for s in self.style: 50 | s.before(win, state) 51 | def key(self, win, state): 52 | if self.condition == False: 53 | return '' 54 | style_key = ','.join([s.key(win, state) for j, s in enumerate(self.style)]) 55 | if len(style_key) == 0: 56 | return '' 57 | return 'I-' + ','.join(self.include.values()) + '(' \ 58 | + style_key + ')' 59 | def modify(self, hl, to_hl): 60 | if self.condition == False: 61 | return 62 | # we don't need to foce set the bg,ctermbg here for Vim as targetting works slightly differently 63 | if hl['name'] in self.include: 64 | for s in self.style: 65 | s.modify(hl, to_hl) 66 | self.attach = __Include 67 | -------------------------------------------------------------------------------- /lib/vimade/style/invert.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | M = sys.modules[__name__] 4 | 5 | from vimade.util import color as COLOR_UTIL 6 | from vimade.style.value import condition as CONDITION 7 | from vimade.util import type as TYPE 8 | from vimade.util import validate as VALIDATE 9 | 10 | GLOBALS = None 11 | 12 | def __init(args): 13 | global GLOBALS 14 | GLOBALS = args['GLOBALS'] 15 | M.__init = __init 16 | 17 | # @required 18 | # number | { 19 | # @optional 'fg': 0-1, # applies the given intensity to text 20 | # @optional 'bg': 0-1, # applies the given intensity to background 21 | # @optional 'sp': 0-1, # applies the given intensity to special 22 | # } 23 | class Invert(): 24 | def __init__(parent, **kwargs): 25 | _condition = kwargs.get('condition') 26 | _condition = _condition if _condition != None else CONDITION.INACTIVE 27 | parent._value = kwargs.get('value') 28 | parent.tick = kwargs.get('tick') 29 | class __Invert(): 30 | def __init__(self, win, state): 31 | self.win = win 32 | self._condition = _condition 33 | self.condition = None 34 | self.invert = parent._value 35 | self._animating = False 36 | def resolve(self, value, state): 37 | return VALIDATE.invert(TYPE.resolve_all_fn(value, self, state)) 38 | def before(self, win, state): 39 | invert = self.resolve(parent._value, state) 40 | self.condition = _condition(self, state) if callable(_condition) else _condition 41 | if self.condition == False: 42 | return 43 | self.invert = invert 44 | def key(self, win, state): 45 | if self.condition == False or not self.invert: 46 | return '' 47 | # TODO shrink 48 | return 'INV-' + str(self.invert.get('fg',0)) + '-' + str(self.invert.get('bg',0)) + '-' + str(self.invert.get('sp',0)) 49 | def modify(self, hl, to_hl): 50 | if self.condition == False or not self.invert: 51 | return 52 | invert = self.invert 53 | for hi in (hl, to_hl): 54 | for key in ('fg', 'bg', 'sp'): 55 | color = hi.get(key) 56 | if color != None: 57 | hi[key] = COLOR_UTIL.interpolate24b(color, 0XFFFFFF - color, 1 - invert[key]) 58 | for (key, i_key) in (('ctermfg', 'fg'), ('ctermbg', 'bg')): 59 | color = hi.get(key) 60 | if color != None: 61 | color = COLOR_UTIL.toRgb(color, True) 62 | target = [255 - color[0], 255 - color[1], 255 - color[2]] 63 | hi[key] = COLOR_UTIL.interpolate256(color, target, 1 - invert[i_key]) 64 | parent.attach = __Invert 65 | def value(parent, replacement = None): 66 | if replacement != None: 67 | parent._value = replacement 68 | return parent 69 | return parent._value 70 | -------------------------------------------------------------------------------- /lib/vimade/style/tint.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade.util import color as COLOR_UTIL 5 | from vimade.style.value import condition as CONDITION 6 | from vimade.util import type as TYPE 7 | from vimade.util import validate as VALIDATE 8 | 9 | GLOBALS = None 10 | 11 | def __init(args): 12 | global GLOBALS 13 | GLOBALS = args['GLOBALS'] 14 | M.__init = __init 15 | 16 | def _tint_or_global(tint): 17 | if type(tint) == dict: 18 | return TYPE.deep_copy(tint) 19 | return GLOBALS.tint 20 | 21 | def _create_tint(tint): 22 | if type(tint) != dict: 23 | return None 24 | fg = tint.get('fg') 25 | bg = tint.get('bg') 26 | sp = tint.get('sp') 27 | if not fg and not bg and not sp: 28 | return None 29 | result = { 30 | 'fg': None, 31 | 'bg': None, 32 | 'sp': None, 33 | 'ctermfg': None, 34 | 'ctermbg': None, 35 | 'fg_intensity': None, 36 | 'bg_intensity': None, 37 | 'sp_intensity': None, 38 | } 39 | if fg: 40 | rgb = fg.get('rgb') 41 | result['fg'] = COLOR_UTIL.to24b(rgb) 42 | result['ctermfg'] = COLOR_UTIL.toRgb(rgb) 43 | result['fg_intensity'] = 1 - fg.get('intensity') 44 | if bg: 45 | rgb = bg.get('rgb') 46 | result['bg'] = COLOR_UTIL.to24b(rgb) 47 | result['ctermbg'] = COLOR_UTIL.toRgb(rgb) 48 | result['bg_intensity'] = 1 - bg.get('intensity') 49 | if sp: 50 | result['sp'] = COLOR_UTIL.to24b(sp.get('rgb')) 51 | result['sp_intensity'] = 1 - sp.get('intensity') 52 | return result 53 | 54 | # @param **kwargs { 55 | # condition, 56 | # value = { 57 | # 'fg': {'rgb': [255,255,255], 'intensity': 0-1}, 58 | # 'bg': {'rgb': [255,255,255], 'intensity': 0-1}, 59 | # 'sp': {'rgb': [255,255,255], 'intensity': 0-1}, 60 | # } 61 | #} 62 | # rgb value for each fg, bg, and sp. 63 | # These are optional and you can choose which ones that you want to specify. 64 | # intensity is 0-1 (1 being the most amount of recoloring applied) 65 | class Tint(): 66 | def __init__(parent, **kwargs): 67 | _condition = kwargs.get('condition') 68 | _condition = _condition if _condition != None else CONDITION.INACTIVE 69 | parent._value = kwargs.get('value') 70 | parent.tick = kwargs.get('tick') 71 | class __Tint(): 72 | def __init__(self, win, state): 73 | value = parent._value 74 | self.win = win 75 | self._condition = _condition 76 | self.condition = None 77 | self.to_hl = None 78 | self._animating = False 79 | def resolve(self, value, state): 80 | return VALIDATE.tint(TYPE.resolve_all_fn(value, self, state)) 81 | def before(self, win, state): 82 | # don't use self.resolve here for performance reasons 83 | tint = TYPE.resolve_all_fn(parent._value, self, state) 84 | self.condition = _condition(self, state) if callable(_condition) else _condition 85 | if self.condition == False: 86 | return 87 | self.to_hl = _create_tint(VALIDATE.tint(tint)) 88 | def key(self, win, state): 89 | if self.condition == False or not self.to_hl: 90 | return '' 91 | to_hl = self.to_hl 92 | return 'T-' \ 93 | + str(to_hl['fg'] != None and (str(to_hl['fg'] or '') + ',' + (str(to_hl['ctermfg'][0])+'-'+str(to_hl['ctermfg'][1])+'-'+str(to_hl['ctermfg'][2])) + ',' + str(to_hl['fg_intensity'])) or '') + '|' \ 94 | + (to_hl['bg'] != None and (str(to_hl['bg'] or '') + ',' + (str(to_hl['ctermbg'][0])+'-'+str(to_hl['ctermbg'][1])+'-'+str(to_hl['ctermbg'][2])) + ',' + str(to_hl['bg_intensity'])) or '') + '|' 95 | + (to_hl['sp'] != None and (str(to_hl['sp'] or '') + str(to_hl['sp_intensity'])) or '') 96 | def modify(self, hl, target): 97 | if self.condition == False: 98 | return 99 | to_hl = self.to_hl 100 | if not to_hl: 101 | return 102 | if target['bg'] != None and to_hl['bg'] != None: 103 | target['bg'] = COLOR_UTIL.interpolate24b(target['bg'], to_hl['bg'], to_hl['bg_intensity']) 104 | if target['ctermbg'] != None and to_hl['ctermbg'] != None: 105 | target['ctermbg'] = COLOR_UTIL.interpolate256(target['ctermbg'], to_hl['ctermbg'], to_hl['bg_intensity']) 106 | if hl['fg'] != None and to_hl['fg'] !=None: 107 | hl['fg'] = COLOR_UTIL.interpolate24b(hl['fg'], to_hl['fg'], to_hl['fg_intensity']) 108 | if hl['bg'] != None and to_hl['bg'] != None: 109 | hl['bg'] = COLOR_UTIL.interpolate24b(hl['bg'], to_hl['bg'], to_hl['bg_intensity']) 110 | if hl['sp'] != None and to_hl['sp'] != None: 111 | hl['sp'] = COLOR_UTIL.interpolate24b(hl['sp'], to_hl['sp'], to_hl['sp_intensity']) 112 | if hl['ctermfg'] != None and to_hl['ctermfg'] != None: 113 | hl['ctermfg'] = COLOR_UTIL.interpolate256(hl['ctermfg'], to_hl['ctermfg'], to_hl['fg_intensity']) 114 | if hl['ctermbg'] != None and to_hl['ctermbg'] != None: 115 | hl['ctermbg'] = COLOR_UTIL.interpolate256(hl['ctermbg'], to_hl['ctermbg'], to_hl['bg_intensity']) 116 | return 117 | parent.attach = __Tint 118 | def value(parent, replacement = None): 119 | if replacement != None: 120 | parent._value = replacement 121 | return parent 122 | return parent._value 123 | 124 | def Default(**kwargs): 125 | return Tint(**TYPE.extend({ 126 | 'condition': CONDITION.INACTIVE, 127 | 'value': lambda style, state: GLOBALS.tint(style, state) if callable(GLOBALS.tint) else _tint_or_global(GLOBALS.tint), 128 | }, kwargs)) 129 | -------------------------------------------------------------------------------- /lib/vimade/style/value/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/style/value/__init__.py -------------------------------------------------------------------------------- /lib/vimade/style/value/animate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | from vimade import animator as ANIMATOR 5 | from vimade.style.value import ease as EASE 6 | from vimade.style.value import condition as CONDITION 7 | from vimade.style.value import direction as DIRECTION 8 | from vimade.style import invert as INVERT 9 | from vimade.util import type as TYPE 10 | from vimade.util import color as COLOR_UTIL 11 | 12 | DEFAULT_DURATION = 300 13 | DEFAULT_DELAY = 0 14 | DEFAULT_EASE = EASE.OUT_QUART 15 | DEFAULT_DIRECTION = DIRECTION.OUT 16 | 17 | def __init(args): 18 | global GLOBALS 19 | GLOBALS = args['GLOBALS'] 20 | M.__init = __init 21 | 22 | ids = 0 23 | def get_next_id(): 24 | global ids 25 | ids = ids + 1 26 | return ids 27 | 28 | M.EASE = EASE 29 | M.DIRECTION = DIRECTION 30 | 31 | def _compare_eq(value, last): 32 | return value == last 33 | 34 | def Number(**kwargs): 35 | def interpolate(to, start, pct_time, style, state): 36 | return COLOR_UTIL.interpolateFloat(to, start, pct_time) 37 | kwargs['interpolate'] = interpolate 38 | kwargs['compare'] = _compare_eq 39 | return M.Animate(**kwargs) 40 | 41 | def Rgb(**kwargs): 42 | def interpolate(to, start, pct_time, style, state): 43 | return COLOR_UTIL.interpolateRgb(to, start, pct_time) 44 | kwargs['interpolate'] = interpolate 45 | kwargs['compare'] = TYPE.shallow_compare 46 | return M.Animate(**kwargs) 47 | 48 | def Invert(**kwargs): 49 | def interpolate(to, start, pct_time, style, state): 50 | result = {} 51 | to = to or {} 52 | start = start or {} 53 | result['fg'] = COLOR_UTIL.interpolateFloat(to.get('fg', 0), start.get('fg', 0), pct_time) 54 | result['bg'] = COLOR_UTIL.interpolateFloat(to.get('bg', 0), start.get('bg', 0), pct_time) 55 | result['sp'] = COLOR_UTIL.interpolateFloat(to.get('sp', 0), start.get('sp', 0), pct_time) 56 | return result 57 | kwargs['interpolate'] = interpolate 58 | kwargs['compare'] = TYPE.deep_compare 59 | return M.Animate(**kwargs) 60 | 61 | def Tint(**kwargs): 62 | def interpolate(to, start, pct_time, style, state): 63 | if not start and not to: 64 | return None 65 | to = to or {} 66 | start = start or {} 67 | result = {} 68 | for key, value in to.items(): 69 | if not key in start: 70 | start[key] = {'rgb': value['rgb'], 'intensity': 0} 71 | for key, value in start.items(): 72 | if not key in to: 73 | to[key] = {'rgb': value['rgb'], 'intensity': 0} 74 | for key, value in to.items(): 75 | result[key] = { 76 | 'rgb': COLOR_UTIL.interpolateRgb(value['rgb'], start[key]['rgb'], pct_time), 77 | 'intensity': COLOR_UTIL.interpolateFloat(value['intensity'], start[key]['intensity'], pct_time), 78 | } 79 | return result 80 | kwargs['interpolate'] = interpolate 81 | kwargs['compare'] = TYPE.deep_compare 82 | return M.Animate(**kwargs) 83 | 84 | # @param kwargs { 85 | # @required to = number | function -> number 86 | # @required interpolate = function(pct_time, start_value, to_value) -> value 87 | # @optional id: string | number | function -> string = 0 -- used to share state between values that might cross over between filters, exclusions, and other rules 88 | # @optional start: number | function -> number = 0 89 | # @optional duration: number | function -> number = 300 90 | # @optional delay: number | function -> number = 0 91 | # @optional ease: EASE | function -> EASE = EASE.OUT_QUART 92 | # @optional direction: DIRECTION | function -> DIRECTION 93 | #} 94 | def Animate(**kwargs): 95 | _custom_id = kwargs.get('id') 96 | _id = _custom_id if _custom_id != None else get_next_id() 97 | _interpolate = kwargs.get('interpolate') 98 | _start = kwargs.get('start') 99 | _to = kwargs.get('to') 100 | _duration = kwargs.get('duration') 101 | _duration = _duration if _duration != None else DEFAULT_DURATION 102 | _delay = kwargs.get('delay') 103 | _delay = _delay if _delay != None else DEFAULT_DELAY 104 | _ease = kwargs.get('ease') 105 | _ease = _ease if _ease != None else DEFAULT_EASE 106 | _direction = kwargs.get('direction') 107 | _direction = _direction if _direction != None else DEFAULT_DIRECTION 108 | _reset = kwargs.get('reset') 109 | _reset = _reset if _reset != None else (_direction != DIRECTION.IN_OUT) 110 | _compare = kwargs.get('compare') 111 | def animate(style, state): 112 | id = None 113 | win = style.win 114 | if _custom_id != None: 115 | state = state['custom'] 116 | id = _custom_id(win_state) if callable(_custom_id) else _custom_id 117 | else: 118 | state = state['animations'] 119 | id = _id 120 | if not id in state: 121 | state[id] = {'value': None} 122 | state = state[id] 123 | to = style.resolve(_to, state) 124 | start = style.resolve(_start, state) 125 | compare = _compare(to, state.get('last_to')) if callable(_compare) else _compare 126 | delay = _delay(style, state) if callable(_delay) else _delay 127 | duration = _duration(style, state) if callable(_duration) else _duration 128 | direction = _direction(style, state) if callable(_direction) else _direction 129 | reset = _reset(style, state) if callable(_reset) else _reset 130 | if compare == False: 131 | state['change_timestamp'] = GLOBALS.now 132 | state['last_to'] = to 133 | time = (GLOBALS.now - (max(win.timestamps['nc'], state.get('change_timestamp') or 0))) * 1000 - (delay or 0) 134 | # direction in and active means go towards 'to' value from start value 135 | # direction out and active means go towards start value when the window becomes inactive 136 | # otherwise the window should be on 'to' value 137 | # TODO abstract this into a behavior template 138 | if (direction == DIRECTION.OUT and style._condition == CONDITION.ACTIVE and not style.win.nc) \ 139 | or (direction == DIRECTION.IN and style._condition == CONDITION.INACTIVE and style.win.nc): 140 | state['value'] = to 141 | state['start'] = to 142 | style._animating = False 143 | return to 144 | elif (direction == DIRECTION.OUT and style._condition == CONDITION.ACTIVE and style.win.nc) \ 145 | or (direction == DIRECTION.IN and style._condition == CONDITION.INACTIVE and not style.win.nc): 146 | swp = start 147 | start = to 148 | to = swp 149 | elif (direction == DIRECTION.OUT and style._condition == CONDITION.INACTIVE and not style.win.nc) \ 150 | or (direction == DIRECTION.IN and style._condition == CONDITION.ACTIVE and style.win.nc): 151 | state['value'] = start 152 | state['start'] = start 153 | style._animating = False 154 | return start 155 | elif (direction == DIRECTION.OUT and style._condition == CONDITION.INACTIVE and style.win.nc) \ 156 | or (direction == DIRECTION.IN and style._condition == CONDITION.ACTIVE and not style.win.nc): 157 | pass 158 | value = state.get('value') 159 | if value == None: 160 | state['value'] = start 161 | state['start'] = start 162 | if time <= 0: 163 | if reset == True: 164 | state['start'] = start 165 | state['value'] = start 166 | else: 167 | state['start'] = state['value'] 168 | style._animating = True 169 | ANIMATOR.schedule(win) 170 | return state['start'] 171 | if time >= duration: 172 | state['value'] = to 173 | style._animating = False 174 | return to 175 | 176 | elapsed = time / float(duration) 177 | elapsed = min(max(_ease(elapsed), 0), 1) 178 | state['value'] = _interpolate(to, state['start'], elapsed, style, state) 179 | ANIMATOR.schedule(win) 180 | style._animating = True 181 | return state['value'] 182 | return animate 183 | -------------------------------------------------------------------------------- /lib/vimade/style/value/condition.py: -------------------------------------------------------------------------------- 1 | # TODO represent these as behaviors 2 | def ACTIVE (style, state): 3 | return style.win.nc != True or style._animating == True 4 | 5 | def INACTIVE (style, state): 6 | return style.win.nc == True or style._animating == True 7 | 8 | def ALL (style, state): 9 | return True 10 | -------------------------------------------------------------------------------- /lib/vimade/style/value/direction.py: -------------------------------------------------------------------------------- 1 | import sys 2 | M = sys.modules[__name__] 3 | 4 | M.IN = 'in' 5 | M.OUT = 'out' 6 | def IN_OUT(style, state): 7 | if style.win.nc == True: 8 | return M.OUT 9 | else: 10 | return M.IN 11 | -------------------------------------------------------------------------------- /lib/vimade/style/value/ease.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def LINEAR(t): 4 | return t 5 | def IN_SINE(t): 6 | return 1 - math.cos((t * math.pi) / 2.0) 7 | def OUT_SINE(t): 8 | return math.sin((t * math.pi) / 2.0) 9 | def IN_OUT_SINE(t): 10 | return -(math.cos(math.pi*t) - 1) / 2.0 11 | def IN_QUAD(t): 12 | return t * t 13 | def OUT_QUAD(t): 14 | return 1 - (1 - t) * (1 - t) 15 | def IN_OUT_QUAD(t): 16 | if t < 0.5: 17 | return 2 * t * t 18 | else: 19 | return 1 - math.pow((-2 * t + 2), 2) / 2.0 20 | def IN_CUBIC(t): 21 | return t * t * t 22 | def OUT_CUBIC(t): 23 | return 1 - math.pow(1 - t, 3) 24 | def IN_OUT_CUBIC(t): 25 | if t < 0.5: 26 | return 4 * t * t * t 27 | else: 28 | return 1 - math.pow(-2 * t + 2, 3) / 3.0 29 | def IN_QUART(t): 30 | return t * t * t * t 31 | def OUT_QUART(t): 32 | return 1 - math.pow(1 - t, 4) 33 | def IN_OUT_QUART(t): 34 | if t < 0.5: 35 | return 8 * t * t * t * t 36 | else: 37 | return 1 - math.pow(-2 * t + 2, 4) / 2.0 38 | def IN_EXPO(t): 39 | if t == 0: 40 | return 0 41 | else: 42 | return math.pow(2, 10 * t - 10) 43 | def OUT_EXPO(t): 44 | if t == 1: 45 | return 1 46 | else: 47 | return 1 - math.pow(2, -10 * t) 48 | def IN_OUT_EXPO(t): 49 | if t == 0 or t == 1: 50 | return t 51 | elif t < 0.5: 52 | return math.pow(2, 20 * t - 10) / 2.0 53 | else: 54 | return (2 - math.pow(2, -20 * t + 10)) / 2.0 55 | def IN_CIRC(t): 56 | return 1 - math.sqrt(1 - t * t) 57 | def OUT_CIRC(t): 58 | return math.sqrt(1 - math.pow(t - 1, 2)) 59 | def IN_OUT_CIRC(t): 60 | if t < 0.5: 61 | return (1 - math.sqrt(1 - math.pow(2 * t, 2))) / 2.0 62 | else: 63 | return (math.sqrt(1 - math.pow(-2 * t + 2, 2)) + 1) / 2.0 64 | def IN_BACK(t): 65 | return 2.70158 * t * t * t - 1.70158 * t * t 66 | def OUT_BACK(t): 67 | return 1 + 2.70158 * math.pow(t - 1, 3) + 1.70158 * math.pow(t - 1, 2) 68 | def IN_OUT_BACK(t): 69 | c1 = 1.70158; 70 | c2 = c1 * 1.525; 71 | if t < 0.5: 72 | return (math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2.0 73 | else: 74 | return (math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2.0; 75 | def OUT_BOUNCE(t): 76 | if t < 1 / 2.75: 77 | return 7.5625 * t * t 78 | elif t < 2 / 2.75: 79 | t = t - 1.5 / 2.75 80 | return 7.5625 * t * t + 0.75 81 | elif t < 2.5 / 2.75: 82 | t = t - 2.25 / 2.75 83 | return 7.5625 * t * t + 0.9375 84 | else: 85 | t = t - 2.625 / 2.75 86 | return 7.5625 * t * t + 0.984375 87 | def IN_BOUNCE(t): 88 | return 1 - OUT_BOUNCE(1 - t) 89 | def IN_OUT_BOUNCE(t): 90 | if t < 0.5: 91 | return (1 - M.OUT_BOUNCE(1 - 2 * t)) / 2.0 92 | else: 93 | return (1 + M.OUT_BOUNCE(2 * t - 1)) / 2.0 94 | -------------------------------------------------------------------------------- /lib/vimade/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaDaa/vimade/7a7252406918c60992c01fd9d8ad08152815ba67/lib/vimade/util/__init__.py -------------------------------------------------------------------------------- /lib/vimade/util/ipc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from vimade.util.promise import Promise 3 | M = sys.modules[__name__] 4 | import vim 5 | 6 | GLOBALS = None 7 | IS_V3 = False 8 | if (sys.version_info > (3, 0)): 9 | IS_V3 = True 10 | 11 | def __init(args): 12 | global GLOBALS 13 | GLOBALS = args['GLOBALS'] 14 | 15 | IS_NVIM = int(vim.eval('has("nvim")')) == 1 16 | DICTIONARY = (dict, vim.Dictionary) if hasattr(vim, 'Dictionary') else dict 17 | LIST = (list, vim.List) if hasattr(vim, 'List') else list 18 | 19 | def py2_coerceTypes (input): 20 | if isinstance(input, (bytes, bytearray)): 21 | return str(input) 22 | elif isinstance(input, DICTIONARY): 23 | result = {} 24 | for key in input.keys(): 25 | result[key] = coerceTypes(input.get(key)) 26 | return result 27 | elif isinstance(input, LIST): 28 | return [coerceTypes(x) for x in list(input)] 29 | return input 30 | 31 | def py3_coerceTypes (input): 32 | if isinstance(input, (bytes, bytearray)): 33 | return str(input, 'utf-8') 34 | elif isinstance(input, DICTIONARY): 35 | result = {} 36 | for key in input.keys(): 37 | if isinstance(key, (bytes, bytearray)): 38 | key = str(key, 'utf-8') 39 | result[key] = coerceTypes(input.get(key)) 40 | return result 41 | elif isinstance(input, LIST): 42 | return [coerceTypes(x) for x in list(input)] 43 | return input 44 | 45 | coerceTypes = py3_coerceTypes if IS_V3 else py2_coerceTypes 46 | 47 | def _vim_mem_safe_eval (statement): 48 | vim.command('unlet g:vimade_eval_ret | let g:vimade_eval_ret=' + statement) 49 | return coerceTypes(vim.vars['vimade_eval_ret']) 50 | 51 | def _vim_eval_and_return (statement): 52 | vim.command('unlet g:vimade_eval_ret | let g:vimade_eval_ret=' + statement + '') 53 | return vim.eval('g:vimade_eval_ret') 54 | 55 | eval_and_return = None 56 | mem_safe_eval = None 57 | 58 | if IS_NVIM: 59 | eval_and_return = vim.eval 60 | mem_safe_eval = vim.eval 61 | else: 62 | eval_and_return = _vim_eval_and_return 63 | mem_safe_eval = _vim_mem_safe_eval 64 | 65 | M._batch_cmd = [] 66 | M._batch_cmd_promises = [] 67 | M._batch_eval = [] 68 | M._batch_eval_promises = [] 69 | 70 | def batch_command(statement): 71 | if GLOBALS.disablebatch: 72 | vim.command(statement) 73 | return Promise().resolve(None) 74 | promise = Promise() 75 | M._batch_cmd.append(statement) 76 | M._batch_cmd_promises.append(promise) 77 | return promise 78 | 79 | def batch_eval_and_return(statement): 80 | if GLOBALS.disablebatch: 81 | return Promise().resolve(eval_and_return(statement)) 82 | promise = Promise() 83 | M._batch_eval.append(statement) 84 | M._batch_eval_promises.append(promise) 85 | return promise 86 | 87 | def flush_batch(): 88 | eval = M._batch_eval 89 | eval_promises = M._batch_eval_promises 90 | cmd = M._batch_cmd 91 | cmd_promises = M._batch_cmd_promises 92 | promise = Promise() 93 | 94 | M._batch_eval = [] 95 | M._batch_eval_promises = [] 96 | M._batch_cmd = [] 97 | M._batch_cmd_promises = [] 98 | 99 | if len(cmd): 100 | vim.command('\n'.join(cmd)) 101 | for i, p in enumerate(cmd_promises): 102 | p.resolve(None) 103 | 104 | if len(eval): 105 | results = M.eval_and_return('[' + ','.join(eval) + ']') 106 | for i, result in enumerate(results): 107 | eval_promises[i].resolve(result) 108 | 109 | if len(M._batch_eval) or len(M._batch_cmd): 110 | flush_batch().then(promise) 111 | else: 112 | promise.resolve(None) 113 | return promise 114 | -------------------------------------------------------------------------------- /lib/vimade/util/promise.py: -------------------------------------------------------------------------------- 1 | # Simple Promise helpers to assist with batching IPC 2 | 3 | def all(promises): 4 | cnt = [len(promises)] 5 | result = Promise() 6 | values = [None] * cnt[0] 7 | def scope_reduce(i, promise): 8 | def reduce(value): 9 | values[i] = value 10 | cnt[0] -= 1 11 | if cnt[0] == 0: 12 | result.resolve(values) 13 | promise.then(reduce) 14 | if len(promises) > 0: 15 | for i, promise in enumerate(promises): 16 | scope_reduce(i, promise) 17 | else: 18 | result.resolve([]) 19 | return result 20 | 21 | 22 | class Promise: 23 | def __init__(self): 24 | self._has_value = False 25 | self._value = None 26 | self._callbacks = None 27 | def resolve(self, value): 28 | self._has_value = True 29 | self._value = value 30 | if self._callbacks: 31 | for callback in self._callbacks: 32 | if callable(callback): 33 | callback(value) 34 | else: 35 | callback.resolve(value) 36 | self._callbacks = None 37 | return self 38 | def then(self, callback): 39 | if self._has_value: 40 | if callable(callback): 41 | callback(self._value) 42 | else: 43 | callback.resolve(self._value) 44 | elif not self._callbacks: 45 | self._callbacks = [callback] 46 | else: 47 | self._callbacks.append(callback) 48 | return self 49 | -------------------------------------------------------------------------------- /lib/vimade/util/type.py: -------------------------------------------------------------------------------- 1 | def get(obj, key): 2 | if type(obj) == dict: 3 | return obj.get(key) 4 | elif type(obj) == list: 5 | if key < len(obj): 6 | return obj(key) 7 | return None 8 | 9 | def pairs(obj): 10 | if type(obj) == dict: 11 | return obj.items() 12 | elif type(obj) in (list, tuple): 13 | return enumerate(obj) 14 | return enumerate([]) 15 | 16 | def shallow_compare(left, right): 17 | if left == None or right == None: 18 | return left == right 19 | for k, v in pairs(left): 20 | if get(right, k) != v: 21 | return False 22 | for k, v in pairs(right): 23 | if get(left, k) != v: 24 | return False 25 | return True 26 | 27 | def deep_compare(left, right): 28 | if left == None or right == None: 29 | return left == right 30 | copy ={} 31 | for key, value in pairs(left): 32 | copy[key] = value 33 | for key, value in pairs(right): 34 | copy_value = get(copy, key) 35 | if copy_value == None: 36 | return False 37 | elif type(copy_value) != type(value): 38 | return False 39 | elif type(value) in (dict, list, tuple): 40 | if deep_compare(copy_value, value) == True: 41 | del copy[key] 42 | else: 43 | return False 44 | elif copy_value == value: 45 | del copy[key] 46 | else: 47 | return False 48 | if len(copy.keys()) > 0: 49 | return False 50 | return True 51 | 52 | for k, v in pairs(left): 53 | if get(right, k) != v: 54 | return False 55 | for k, v in pairs(right): 56 | if get(left, k) != v: 57 | return False 58 | 59 | 60 | def deep_copy(obj): 61 | # disallow tuple 62 | if type(obj) in (list, tuple): 63 | return [deep_copy(v) for v in obj] 64 | elif type(obj) == dict: 65 | result = {} 66 | for key, value in obj.items(): 67 | result[key] = deep_copy(value) 68 | return result 69 | else: 70 | return obj 71 | 72 | def shallow_copy(obj): 73 | return shallow_extend({}, obj) 74 | 75 | def shallow_extend(base, *args): 76 | for target in args: 77 | if type(target) == dict: 78 | for key, value in target.items(): 79 | base[key] = value 80 | return base 81 | extend = shallow_extend 82 | 83 | def deep_extend(base, *args): 84 | base = deep_copy(base) 85 | for target in args: 86 | target = deep_copy(target) 87 | shallow_extend(base, target) 88 | return base 89 | 90 | def resolve_all_fn(obj, style, state): 91 | if callable(obj): 92 | obj = obj(style, state) 93 | if type(obj) == dict: 94 | copy = {} 95 | for k, v in obj.items(): 96 | copy[k] = resolve_all_fn(v, style, state) 97 | return copy 98 | return obj 99 | -------------------------------------------------------------------------------- /lib/vimade/util/validate.py: -------------------------------------------------------------------------------- 1 | from vimade.util import color as COLOR_UTIL 2 | 3 | def range(value, min_value, max_value, default = 0): 4 | if type(value) not in (int, float): 5 | try: 6 | value = float(value) 7 | except: 8 | return default 9 | return min(max(value, min_value), max_value) 10 | 11 | def fade(value): 12 | result = range(value, 0, 1, 0.4) 13 | return result 14 | 15 | def intensity(value): 16 | return range(value, 0, 1, 1) 17 | 18 | def rgb(value): 19 | if type(value) not in (list, tuple) or len(value) < 3: 20 | return None 21 | return [ 22 | range(value[0], 0, 255, 0), 23 | range(value[1], 0, 255, 0), 24 | range(value[2], 0, 255, 0)] 25 | 26 | def color(value, is256 = False): 27 | if type(value) in (list, tuple): 28 | return rgb(value) 29 | if type(value) == str: 30 | value = COLOR_UTIL.to24b(value) 31 | if is256: 32 | return range(value, 0, 255, 0) 33 | else: 34 | return range(value, 0, 0xFFFFFF, 0) 35 | 36 | def invert(value): 37 | if type(value) in (int, float): 38 | value = range(value, 0, 1, 0) 39 | return {'fg': value, 'bg': value, 'sp': value} 40 | if type(value) == dict: 41 | value['fg'] = range(value.get('fg'), 0, 1, 0) 42 | value['bg'] = range(value.get('bg'), 0, 1, 0) 43 | value['sp'] = range(value.get('sp'), 0, 1, 0) 44 | return value 45 | return {'fg': 0, 'bg': 0, 'sp': 0} 46 | 47 | 48 | def tint_attr(value): 49 | if type(value) != dict: 50 | return None 51 | rgb = value.get('rgb') 52 | intensity_value = value.get('intensity') 53 | if rgb != None: 54 | value['rgb'] = rgb = color(rgb) 55 | if type(rgb) != list: 56 | value['rgb'] = COLOR_UTIL.toRgb(rgb) 57 | value['intensity'] = intensity(intensity_value) 58 | elif intensity_value != None: 59 | value['intensity'] = intensity(intensity_value) 60 | if value.get('rgb') != None and value.get('intensity') != None: 61 | return value 62 | return None 63 | 64 | def tint(value): 65 | if type(value) != dict: 66 | return None 67 | for (k, v) in list(value.items()): 68 | if k not in ('fg', 'bg', 'sp'): 69 | del value[k] 70 | else: 71 | attr = tint_attr(v) 72 | if attr == None: 73 | del value[k] 74 | else: 75 | value[k] = attr 76 | return value 77 | -------------------------------------------------------------------------------- /lua/vimade/animator.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local events = require('vimade.util.events')() 4 | 5 | M.on = events.on 6 | 7 | local scheduled = nil 8 | local animating = nil 9 | 10 | M.__init = function (args) 11 | args.FADER.on('tick:before', function() 12 | if scheduled then 13 | animating = scheduled 14 | scheduled = nil 15 | end 16 | end) 17 | args.FADER.on('tick:after', function () 18 | if animating then 19 | -- TODO this isn't explicitly needed, but we should add something similar to this 20 | -- animations should be set back to false if the window condition is no longer active 21 | -- for winid, styles in pairs(animating) do 22 | -- local scheduled_styles = (scheduled or {})[winid] or {} 23 | -- for _, style in ipairs(styles) do 24 | -- local found = false 25 | -- for __, style2 in ipairs(scheduled_styles) do 26 | -- if style == style2 then 27 | -- found = true 28 | -- break 29 | -- end 30 | -- end 31 | -- -- ensure we unset animating for anything that didn't re-schedule 32 | -- if not found and style._animating then 33 | -- style._animating = false 34 | -- end 35 | -- end 36 | -- end 37 | animating = nil 38 | end 39 | if scheduled then 40 | vim.schedule(vim.fn['vimade#StartAnimationTimer']) 41 | end 42 | events.notify('animator:after') 43 | end) 44 | end 45 | 46 | M.schedule = function (style) 47 | -- this is also doable via lua code and then scheduling a vim-safe callback 48 | -- but there appears to be no benefit. Sharing the code across all supported 49 | -- versions seems much more maintainable currently AND 50 | -- there also seems to be much less flickering with this approach when compared 51 | -- to the lua alternative. 52 | if not scheduled then 53 | scheduled = {} 54 | end 55 | if not scheduled[style.win.winid] then 56 | scheduled[style.win.winid] = {} 57 | end 58 | table.insert(scheduled[style.win.winid], style) 59 | end 60 | 61 | M.is_animating = function(winid) 62 | return (scheduled and scheduled[winid]) or (animating and animating[winid]) or false 63 | end 64 | 65 | return M 66 | -------------------------------------------------------------------------------- /lua/vimade/config_helpers/blocklist.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local MATCHERS = require('vimade.util.matchers') 4 | local GLOBALS = require('vimade.state.globals') 5 | 6 | local TABLE_INSERT = table.insert 7 | 8 | local minimap_matcher = MATCHERS.StringMatcher('-minimap') 9 | local highlight_key_reducer = require('vimade.util.key_reducer')() 10 | 11 | M.DEFAULT = function (win, active, config) 12 | local legacy 13 | if GLOBALS.fademinimap == false or config.buf_name then 14 | legacy = (GLOBALS.fademinimap == false and minimap_matcher(win.buf_name)) 15 | end 16 | return 17 | legacy 18 | or (config.buf_name and MATCHERS.ContainsString(config.buf_name)(win.buf_name)) 19 | or (config.win_type and MATCHERS.ContainsString(config.win_type)(win.win_type)) 20 | or (config.buf_opts and MATCHERS.ContainsAny(config.buf_opts)(win.buf_opts)) 21 | or (config.buf_vars and MATCHERS.ContainsAny(config.buf_vars)(win.buf_vars)) 22 | or (config.win_opts and MATCHERS.ContainsAny(config.win_opts)(win.win_opts)) 23 | or (config.win_vars and MATCHERS.ContainsAny(config.win_vars)(win.win_vars)) 24 | or (config.win_config and MATCHERS.ContainsAny(config.win_config)(win.win_config)) 25 | end 26 | 27 | M.TO_HIGHLIGHTS_KEY = function(blocked_highlights) 28 | local result1 = highlight_key_reducer.reduce_ipairs(blocked_highlights.exact) 29 | local result2 = highlight_key_reducer.reduce_ipairs(blocked_highlights.pattern) 30 | return result1 .. '+' .. result2 31 | end 32 | 33 | M.HIGHLIGHTS = function(win, active) 34 | -- result is a simple map of names that are blocked 35 | local result = {exact = {}, pattern = {}} 36 | local exact = result.exact 37 | local pattern = result.pattern 38 | local include = function(name) 39 | if name:sub(1,1) == '/' and name:sub(-1) == '/' then 40 | TABLE_INSERT(pattern, name:sub(2,-2)) 41 | else 42 | TABLE_INSERT(exact, name) 43 | end 44 | end 45 | if GLOBALS.blocklist then 46 | for rule_name, rule in pairs(GLOBALS.blocklist) do 47 | if type(rule) == 'table' and rule.highlights then 48 | for key, value in pairs(rule.highlights) do 49 | if type(value) == 'function' then 50 | value = value(win, active) 51 | if type(value) == 'string' then 52 | include(value) 53 | elseif type(value) == 'table' then 54 | for k, v in pairs(value) do 55 | include(v) 56 | end 57 | end 58 | else 59 | include(value) 60 | end 61 | end 62 | end 63 | end 64 | end 65 | return result 66 | end 67 | 68 | return M 69 | -------------------------------------------------------------------------------- /lua/vimade/config_helpers/link.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local MATCHERS = require('vimade.util.matchers') 4 | local GLOBALS = require('vimade.state.globals') 5 | 6 | M.DEFAULT = function(win, active, config) 7 | local legacy = false 8 | if GLOBALS.groupdiff == true or GLOBALS.groupscrollbind == true then 9 | local win_wo = win.win_opts 10 | local active_wo = active.win_opts 11 | legacy = (GLOBALS.groupdiff and MATCHERS.EachContainsAny({diff = true})({active_wo, win_wo})) 12 | or (GLOBALS.groupscrollbind and MATCHERS.EachContainsAny({scrollbind = true})({active_wo, win_wo})) 13 | end 14 | return 15 | legacy 16 | or (config.buf_name and MATCHERS.ContainsString(config.buf_name)(win.buf_name) and MATCHERS.ContainsString(config.buf_name)(active.buf_name)) 17 | or (config.win_type and MATCHERS.ContainsString(config.win_type)(win.win_type) and MATCHERS.ContainsString(config.win_type)(active.win_type)) 18 | or (config.buf_opts and MATCHERS.EachContainsAny(config.buf_opts)({active.buf_opts, win.buf_opts})) 19 | or (config.buf_vars and MATCHERS.EachContainsAny(config.buf_vars)({active.buf_vars, win.buf_vars})) 20 | or (config.win_opts and MATCHERS.EachContainsAny(config.win_opts)({active.win_opts, win.win_opts})) 21 | or (config.win_vars and MATCHERS.EachContainsAny(config.win_vars)({active.win_vars, win.win_vars})) 22 | or (config.win_config and MATCHERS.EachContainsAny(config.win_config)({active.win_config, win.win_config})) 23 | end 24 | 25 | return M 26 | -------------------------------------------------------------------------------- /lua/vimade/fader.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local bit = require('bit') 3 | local BIT_BAND = bit.band 4 | local BIT_BOR = bit.bor 5 | 6 | local ANIMATE = require('vimade.style.value.animate') 7 | local ANIMATOR = require('vimade.animator') 8 | local COMPAT = require('vimade.util.compat') 9 | local FADE = require('vimade.style.fade') 10 | local CONDITION = require('vimade.style.value.condition') 11 | local DEFAULT = require('vimade.recipe.default') 12 | local GLOBALS = require('vimade.state.globals') 13 | local HIGHLIGHTER = require('vimade.highlighters.namespace') 14 | local FOCUS = require('vimade.focus') 15 | local NAMESPACE = require('vimade.state.namespace') 16 | local REAL_NAMESPACE = require('vimade.state.real_namespace') 17 | local TINT = require('vimade.style.tint') 18 | local WIN_STATE = require('vimade.state.win') 19 | 20 | local TABLE_INSERT = table.insert 21 | 22 | local events = require('vimade.util.events')() 23 | 24 | M.on = events.on 25 | 26 | -- internal only 27 | local update = function () 28 | local windows = vim.api.nvim_tabpage_list_wins(0) 29 | local current = GLOBALS.current 30 | local updated_cache = {} 31 | 32 | areas = FOCUS.update({ 33 | winid = GLOBALS.current.winid, 34 | tabnr = GLOBALS.current.tabnr, 35 | prevent_events = false, 36 | }) 37 | 38 | local style = GLOBALS.style 39 | for i, s in ipairs(style) do 40 | if s.tick then 41 | s.tick() 42 | end 43 | end 44 | 45 | if BIT_BAND(GLOBALS.tick_state, GLOBALS.DISCARD_NS) > 0 then 46 | NAMESPACE.discard_all() 47 | end 48 | if current.winid ~= -1 then 49 | WIN_STATE.refresh_active(current.winid) 50 | end 51 | for i, winid in ipairs(windows) do 52 | -- Refresh the non-area windows first. We need to know the state of the area_owner 53 | -- before processing areas. 54 | if current.winid ~= winid and not FOCUS.get(winid) then 55 | WIN_STATE.refresh(winid) 56 | end 57 | end 58 | 59 | -- Now check the areas. 60 | for winid, _ in pairs(areas) do 61 | WIN_STATE.refresh(winid) 62 | TABLE_INSERT(windows, winid) 63 | end 64 | 65 | -- Neovim will sometimes corrupt namespaces. This happens frequently in 0.8.0 and much less 66 | -- on newer versions. Corrupted namespaces results in flickers, lost highlights, or completely 67 | -- incorrect colors. We can detect it by ensuring the first highlight in a namespace (vimade_control) 68 | -- always has our expected color values. When the values are wrong, we need to reset every color 69 | -- in that namespace and force redraw. 70 | local corrupted_namespaces = {} 71 | for i, winid in ipairs(windows) do 72 | local win = WIN_STATE.get(winid) 73 | -- check if the namespace is owned by vimade and whether its currently active 74 | -- ensures that the 75 | if win and win.ns and win.current_ns and win.current_ns == win.ns.vimade_ns then 76 | local result = COMPAT.nvim_get_hl(win.ns.vimade_ns, {name = 'vimade_control'}) 77 | if result.fg ~= 0XFEDCBA or result.bg ~= 0X123456 then 78 | if not corrupted_namespaces[win.ns.vimade_ns] then 79 | corrupted_namespaces[win.ns.vimade_ns] = true 80 | HIGHLIGHTER.set_highlights(win) 81 | end 82 | COMPAT.nvim__redraw({win=winid, valid=false}) 83 | end 84 | end 85 | end 86 | 87 | if BIT_BAND(GLOBALS.tick_state, GLOBALS.CHANGED) > 0 then 88 | local active_winids ={} 89 | local winids = vim.api.nvim_list_wins() 90 | for key, winid in pairs(winids) do 91 | active_winids[winid] = true 92 | end 93 | WIN_STATE.cleanup(active_winids) 94 | FOCUS.cleanup(active_winids) 95 | end 96 | end 97 | 98 | -- external -- 99 | M.setup = function (config) 100 | GLOBALS.setup(config) 101 | FOCUS.setup(config.focus or {}) 102 | end 103 | 104 | M.getInfo = function () 105 | return GLOBALS.getInfo() 106 | end 107 | 108 | M.redraw = function() 109 | M.tick(BIT_BOR(GLOBALS.RECALCULATE, GLOBALS.DISCARD_NS, GLOBALS.CHANGED)) 110 | end 111 | 112 | M.animate = function () 113 | -- animations are monitored via events, no special handling required here 114 | M.tick() 115 | end 116 | 117 | M.tick = function (override_tick_state) 118 | if vim.g.vimade_running == 0 then 119 | return 120 | end 121 | local last_ei = vim.go.ei 122 | vim.go.ei ='all' 123 | 124 | events.notify('tick:before') 125 | GLOBALS.refresh(override_tick_state) 126 | events.notify('tick:refresh') 127 | 128 | update() 129 | 130 | events.notify('tick:after') 131 | vim.go.ei = last_ei 132 | end 133 | 134 | M.disable = function() 135 | M.unhighlightAll() 136 | FOCUS.disable() 137 | end 138 | 139 | M.unhighlightAll = function () 140 | local windows = vim.api.nvim_list_wins() 141 | local current = GLOBALS.current 142 | 143 | for i, winid in pairs(windows) do 144 | local ns = COMPAT.nvim_get_hl_ns({winid = winid}) 145 | if NAMESPACE.is_vimade_ns(ns) == true then 146 | local win = WIN_STATE.get(winid) 147 | if win then 148 | WIN_STATE.unhighlight(win) 149 | end 150 | end 151 | end 152 | end 153 | 154 | FOCUS.on('focus:on', M.tick) 155 | FOCUS.on('focus:off', M.tick) 156 | FOCUS.on('focus:mark', M.tick) 157 | 158 | ANIMATE.__init({FADER=M, GLOBALS=GLOBALS}) 159 | ANIMATOR.__init({FADER=M, GLOBALS=GLOBALS}) 160 | COMPAT.__init({FADER=M, GLOBALS=GLOBALS}) 161 | CONDITION.__init({FADER=M, GLOBALS=GLOBALS}) 162 | DEFAULT.__init({FADER=M, GLOBALS=GLOBALS}) 163 | FADE.__init({FADER=M, GLOBALS=GLOBALS}) 164 | FOCUS.__init({FADER=M, GLOBALS=GLOBALS, WIN_STATE=WIN_STATE}) 165 | HIGHLIGHTER.__init({FADER=M, GLOBALS=GLOBALS}) 166 | REAL_NAMESPACE.__init({FADER=M, GLOBALS=GLOBALS}) 167 | TINT.__init({FADER=M, GLOBALS=GLOBALS}) 168 | WIN_STATE.__init({FADER=M, GLOBALS=GLOBALS}) 169 | 170 | return M 171 | -------------------------------------------------------------------------------- /lua/vimade/focus/api.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local CORE = require('vimade.focus.core') 4 | 5 | local events = require('vimade.util.events')() 6 | 7 | local was_setup = false 8 | local require_setup = function(fn) 9 | return function(arg) 10 | if not was_setup then 11 | M.setup() 12 | end 13 | return fn(arg) 14 | end 15 | end 16 | 17 | M.__init = CORE.__init 18 | 19 | M.on = events.on 20 | 21 | M.get = CORE.get 22 | 23 | M.update = CORE.update 24 | 25 | M.cleanup = CORE.cleanup 26 | 27 | M.setup = function(config) 28 | was_setup = true 29 | CORE.setup(config or {}) 30 | end 31 | 32 | M.global_focus_enabled = require_setup(function() 33 | return CORE.global_focus_enabled 34 | end) 35 | 36 | M.toggle_on = require_setup(function() 37 | if vim.g.vimade_running == 0 then 38 | return 39 | end 40 | CORE.activate_focus() 41 | events.notify('focus:on') 42 | end) 43 | 44 | M.toggle_off = require_setup(function() 45 | CORE.deactivate_focus() 46 | events.notify('focus:off') 47 | end) 48 | 49 | M.toggle = require_setup(function() 50 | if CORE.global_focus_enabled then 51 | M.toggle_off() 52 | else 53 | M.toggle_on() 54 | end 55 | end) 56 | 57 | -- If no range is provided, any marks under the cursor will be removed. 58 | -- If a range is provided, a new mark will be placed. Any marks overlapping the selection will be replaced. 59 | -- config = {@optional range={start, end}} 60 | M.mark_toggle = require_setup(function(config) 61 | CORE.mark_toggle(config) 62 | events.notify('focus:mark') 63 | end) 64 | 65 | -- Places a mark between the range of lines in the window. 66 | -- config = { 67 | -- @optional range={start, end} [default = cursor_location], 68 | -- @optional winid: number [default = vim.api.nvim_get_current_win()] 69 | -- } 70 | M.mark_set = require_setup(function(config) 71 | CORE.mark_set(config) 72 | events.notify('focus:mark') 73 | end) 74 | 75 | -- removes all marks meeting the criteria. If no criteria is included, 76 | -- all marks are removed. 77 | -- config = { 78 | -- @optional range: {start, end} -- NOTE: If range is provided without winid, the current window is assumed. 79 | -- @optional winid: number 80 | -- @optional bufnr: number 81 | -- @optional tabnr: number 82 | -- } 83 | M.mark_remove = require_setup(function(config) 84 | CORE.mark_remove(config) 85 | events.notify('focus:mark') 86 | end) 87 | 88 | M.disable = require_setup(function() 89 | M.toggle_off() 90 | CORE.cleanup({}) 91 | end) 92 | 93 | return M 94 | -------------------------------------------------------------------------------- /lua/vimade/focus/commands.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local API = require('vimade.focus.api') 3 | 4 | local root_options = { 5 | {'VimadeMark', { 6 | {'', function(cmds, arg) API.mark_toggle({range={arg.line1, arg.line2}}) end}, 7 | {'set', function(cmds, arg) API.mark_set({range={arg.line1, arg.line2}}) end}, 8 | {'remove', function(cmds, arg) API.mark_remove({range={arg.line1, arg.line2}}) end}, 9 | {'remove-win', function(cmds, arg) API.mark_remove({winid=vim.api.nvim_get_current_win()}) end}, 10 | {'remove-buf', function(cmds, arg) API.mark_remove({bufnr=vim.api.nvim_get_current_buf()}) end}, 11 | {'remove-tab', function(cmds, arg) API.mark_remove({tabnr=vim.api.nvim_get_current_tabpage()}) end}, 12 | {'remove-all', function(cmds, arg) API.mark_remove({}) end}, 13 | }}, 14 | {'VimadeFocus', { 15 | {'', API.toggle}, 16 | {'toggle', API.toggle}, 17 | {'toggle-on', API.toggle_on}, 18 | {'toggle-off', API.toggle_off}, 19 | }} 20 | } 21 | 22 | local options_of = function(input) 23 | local result = {} 24 | for i, value in ipairs(input) do 25 | if value[1] ~= '' then 26 | table.insert(result, value[1]) 27 | end 28 | end 29 | return result 30 | end 31 | 32 | local process_input = function(input, exact) 33 | local inputs = input:gmatch('([^\\ ]+)') 34 | local options = TYPE.deep_copy(root_options) 35 | local result_options = {} 36 | local remaining = {} 37 | local run = true 38 | local input_size = 0 39 | local ends_sp = input:sub(-1) == ' ' 40 | local process = {} 41 | for input in inputs do 42 | input = input:lower() 43 | input_size = input_size + 1 44 | table.insert(process, input) 45 | end 46 | for i, key in ipairs(process) do 47 | local found = false 48 | for _, option in ipairs(options) do 49 | if key == option[1]:lower() then 50 | found = true 51 | if type(option[2]) == 'function' then 52 | table.insert(result_options, option) 53 | elseif i < input_size or ends_sp or exact then 54 | options = option[2] 55 | else 56 | table.insert(remaining, key) 57 | end 58 | break 59 | end 60 | end 61 | if not exact and not found then 62 | table.insert(remaining, key) 63 | end 64 | end 65 | if #remaining > 1 then 66 | return {} 67 | end 68 | if exact and #result_options > 0 then 69 | options = result_options 70 | end 71 | remaining = remaining[1] 72 | local r_ln = remaining and remaining:len() or 0 73 | local indices = {} 74 | for i, option in ipairs(options) do 75 | indices[option[1]] = i 76 | end 77 | table.sort(options, function(a, b) 78 | local a_is_remaining = a[1]:sub(1, r_ln) == remaining 79 | local b_is_remaining = b[1]:sub(1, r_ln) == remaining 80 | if (a_is_remaining and b_is_remaining) or (not a_is_remaining and not b_is_remaining) then 81 | return indices[a[1]] < indices[b[1]] 82 | elseif a_is_remaining then 83 | return true 84 | elseif b_is_remaining then 85 | return false 86 | end 87 | end) 88 | return options 89 | end 90 | 91 | for _, cmd in ipairs(root_options) do 92 | vim.api.nvim_create_user_command(cmd[1], function(args) 93 | local selection = process_input(args.name .. ' ' .. (args.fargs[1] or ''), true) 94 | local same_level_commands = {} 95 | for _, option in ipairs(selection) do 96 | table.insert(same_level_commands, option[1]) 97 | end 98 | -- only the first is THE selection 99 | if selection[1] and type(selection[1][2]) == 'function' then 100 | selection[1][2](same_level_commands, args) 101 | end 102 | end, { 103 | nargs = '?', 104 | range = true, 105 | complete = function(last_cmd, input) 106 | input = input or '' 107 | input = input:gsub('^\'<,\'>','') 108 | return options_of(process_input(input)) 109 | end 110 | }) 111 | end 112 | -------------------------------------------------------------------------------- /lua/vimade/focus/defaults.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | 3 | local DEFAULTS = { 4 | providers = { 5 | filetypes = { 6 | default = { 7 | -- If you use mini.indentscope, snacks.indent, or hlchunk, you can also highlight the indent scope! 8 | -- {'snacks', {}}, 9 | -- {'mini', {}}, 10 | -- {'hlchunk', {}}, 11 | {'treesitter', { 12 | min_node_size = 2, 13 | min_size = 1, 14 | max_size = 0, 15 | -- exclude types either too large and/or mundane 16 | exclude = { 17 | 'script_file', 18 | 'stream', 19 | 'document', 20 | 'source_file', 21 | 'translation_unit', 22 | 'chunk', 23 | 'module', 24 | 'stylesheet', 25 | 'statement_block', 26 | 'block', 27 | 'pair', 28 | 'program', 29 | 'switch_case', 30 | 'catch_clause', 31 | 'finally_clause', 32 | 'property_signature', 33 | 'dictionary', 34 | 'assignment', 35 | 'expression_statement', 36 | 'compound_statement', 37 | } 38 | }}, 39 | -- if treesitter fails or there isn't a good match, fallback to blanks (similar to limelight) 40 | {'blanks', { 41 | min_size = 1, 42 | max_size = '35%' 43 | }}, 44 | -- if blanks fails to find a good match, fallback to static 35% 45 | {'static', { 46 | size = '35%' 47 | }}, 48 | }, 49 | -- You can make custom configurations for any filetype. Here are some examples. 50 | -- markdown = {{'blanks', {min_size=0, max_size='50%'}}, {'static', {max_size='50%'}}}, 51 | -- javascript = { 52 | -- -- only use treesitter (no fallbacks) 53 | -- {'treesitter', { min_node_size = 2, include = {'if_statement', ...}}}, 54 | -- }, 55 | -- typescript = { 56 | -- {'treesitter', { min_node_size = 2, exclude = {'if_statement'}}}, -- TODO valid & exclue 57 | -- {'static', {size = '35%'}} 58 | -- }, 59 | -- java = { 60 | -- -- mini with a fallback to blanks 61 | -- {'mini', {min_size = 1, max_size = 20}}, 62 | -- {'blanks', {min_size = 1, max_size = '100%' }}, 63 | -- }, 64 | }, 65 | } 66 | } 67 | 68 | local get_providers = function(providers) 69 | local result = {} 70 | for i, provider in ipairs(providers) do 71 | if provider[1] and type(provider[1]) == 'string' then 72 | local name = provider[1]:lower() 73 | local module = require('vimade.focus.providers.' .. name) 74 | table.insert(result, module(provider[2] or {})) 75 | else 76 | table.insert(result, provider) 77 | end 78 | end 79 | return result 80 | end 81 | 82 | return function(config) 83 | config = TYPE.deep_copy(config or {}) or {} 84 | config.providers = config.providers or {} 85 | config.providers.filetypes = config.providers.filetypes or {} 86 | 87 | local filetype_config = config.providers.filetypes 88 | 89 | for ft, ftconfig in pairs(filetype_config) do 90 | filetype_config[ft] = get_providers(ftconfig) 91 | end 92 | for ft, ftconfig in pairs(DEFAULTS.providers.filetypes) do 93 | if not filetype_config[ft] then 94 | filetype_config[ft] = get_providers(ftconfig) 95 | end 96 | end 97 | 98 | return config 99 | end 100 | -------------------------------------------------------------------------------- /lua/vimade/focus/init.lua: -------------------------------------------------------------------------------- 1 | require('vimade.focus.commands') 2 | 3 | -- only expose the api 4 | return require('vimade.focus.api') 5 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/blanks.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | 4 | -- configure through vimade.setup({ 5 | -- focus = { 6 | -- providers = { 7 | -- filetypes = { 8 | -- default = { 9 | -- blanks = { 10 | -- -- anything not included in min_size -> max_size gets dropped, allowing providers to chain 11 | -- max_size is the maximum number of lines allowed to return. If the limit is exceeded, nothing is returned, 12 | -- and the next provider is checked. Default is 0 (infinity). 13 | -- max_size = number [1-N] -> number of lines. 14 | -- number [0.00001-0.99999] or '10%' -> percentage of height 15 | -- 16 | -- min_size is the minimum number of lines allowed to return. If the limit is not reached, nothing is returned, 17 | -- and the next provider is checked. Default is 1. 18 | -- min_size = number [1-N] -> number of lines. 19 | -- number [0.00001-0.99999] or '10%' -> percentage of height 20 | -- } 21 | -- } 22 | -- } 23 | -- } 24 | -- } 25 | -- }) 26 | return function(config) 27 | config = config or {} 28 | if type(config) == 'function' then 29 | config = config(win) 30 | end 31 | 32 | local min_size = TYPE.str_to_pct(config.min_size) 33 | local min_size_is_pct = min_size and true or false 34 | min_size = min_size or tonumber(config.min_size) or 0 35 | min_size_is_pct = min_size_is_pct or (min_size > 0 and min_size < 1) 36 | 37 | local max_size = TYPE.str_to_pct(config.max_size) or 0 38 | local max_size_is_pct = min_size and true or false 39 | max_size = max_size or tonumber(config.max_size) or 0 40 | max_size_is_pct = max_size_is_pct or (max_size > 0 and max_size < 1) 41 | return { 42 | get = function (top, bottom, win) 43 | local min_size_t = min_size 44 | if min_size_is_pct then 45 | min_size_t = MATH_CEIL(win.height * min_size) 46 | end 47 | local max_size_t = max_size 48 | if max_size_is_pct then 49 | max_size_t = MATH_CEIL(win.height * max_size) 50 | end 51 | 52 | local lines = vim.api.nvim_buf_get_lines(win.bufnr, top - 1, bottom - 1, false) 53 | local i = win.cursor[1] - top + 1 54 | local result = {0, 0} 55 | while i >= 1 do 56 | if lines[i] == '' then 57 | break 58 | end 59 | i = i - 1 60 | end 61 | result[1] = i + top - 1 62 | i = win.cursor[1] - top + 1 63 | while i <= (bottom - top) do 64 | if lines[i] == '' then 65 | break 66 | end 67 | i = i + 1 68 | end 69 | result[2] = i + top - 1 70 | local diff = result[2] - result[1] 71 | if (min_size_t > 0 and diff < min_size_t) or (max_size_t > 0 and diff > max_size_t) then 72 | return nil 73 | end 74 | return result 75 | end 76 | } 77 | end 78 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/hlchunk.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | 4 | local HLCHUNK_SCOPE = require('hlchunk.utils.chunkHelper') 5 | 6 | -- configure through vimade.setup({ 7 | -- focus = { 8 | -- providers = { 9 | -- filetypes = { 10 | -- default = { 11 | -- hlchunk = { 12 | -- args = {}, -- merged with hlchunk config (e.g. use_treesitter) 13 | -- -- anything not included in min_size -> max_size gets dropped, allowing providers to chain 14 | -- max_size is the maximum number of lines allowed to return. If the limit is exceeded, nothing is returned, 15 | -- and the next provider is checked. Default is 0 (infinity). 16 | -- max_size = number [1-N] -> number of lines. 17 | -- number [0.00001-0.99999] or '10%' -> percentage of height 18 | -- 19 | -- min_size is the minimum number of lines allowed to return. If the limit is not reached, nothing is returned, 20 | -- and the next provider is checked. Default is 1. 21 | -- min_size = number [1-N] -> number of lines. 22 | -- number [0.00001-0.99999] or '10%' -> percentage of height 23 | -- } 24 | -- } 25 | -- } 26 | -- } 27 | -- } 28 | -- }) 29 | return function(config) 30 | config = config or {} 31 | if type(config) == 'function' then 32 | config = config(win) 33 | end 34 | 35 | local args = config.args or {} 36 | local min_size = TYPE.str_to_pct(config.min_size) 37 | local min_size_is_pct = min_size and true or false 38 | min_size = min_size or tonumber(config.min_size) or 0 39 | min_size_is_pct = min_size_is_pct or (min_size > 0 and min_size < 1) 40 | 41 | local max_size = TYPE.str_to_pct(config.max_size) or 0 42 | local max_size_is_pct = min_size and true or false 43 | max_size = max_size or tonumber(config.max_size) or 0 44 | max_size_is_pct = max_size_is_pct or (max_size > 0 and max_size < 1) 45 | return { 46 | get = function (top, bottom, win) 47 | local min_size_t = min_size 48 | if min_size_is_pct then 49 | min_size_t = MATH_CEIL(win.height * min_size) 50 | end 51 | local max_size_t = max_size 52 | if max_size_is_pct then 53 | max_size_t = MATH_CEIL(win.height * max_size) 54 | end 55 | 56 | local status, scope = HLCHUNK_SCOPE.get_chunk_range(TYPE.extend(args, { 57 | pos = { 58 | bufnr = win.bufnr, 59 | row = win.cursor[1] - 1, 60 | col = win.cursor[2] 61 | } 62 | })) 63 | if status ~= HLCHUNK_SCOPE.CHUNK_RANGE_RET.OK or not scope 64 | or not scope.start or not scope.finish then 65 | return nil 66 | end 67 | local diff = scope.finish - scope.start 68 | if (min_size_t > 0 and diff < min_size_t) or (max_size_t > 0 and diff > max_size_t) then 69 | return nil 70 | end 71 | -- increase each by 1 to show the owner scope lines 72 | return {scope.start + 1, scope.finish + 1} 73 | end 74 | } 75 | end 76 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/mini.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | 4 | local MINI_INDENTSCOPE = require('mini.indentscope') 5 | 6 | -- configure through vimade.setup({ 7 | -- focus = { 8 | -- providers = { 9 | -- filetypes = { 10 | -- default = { 11 | -- mini = { 12 | -- args = {} (config to be merged) 13 | -- -- anything not included in min_size -> max_size gets dropped, allowing providers to chain 14 | -- max_size is the maximum number of lines allowed to return. If the limit is exceeded, nothing is returned, 15 | -- and the next provider is checked. Default is 0 (infinity). 16 | -- max_size = number [1-N] -> number of lines. 17 | -- number [0.00001-0.99999] or '10%' -> percentage of height 18 | -- 19 | -- min_size is the minimum number of lines allowed to return. If the limit is not reached, nothing is returned, 20 | -- and the next provider is checked. Default is 1. 21 | -- min_size = number [1-N] -> number of lines. 22 | -- number [0.00001-0.99999] or '10%' -> percentage of height 23 | -- } 24 | -- } 25 | -- } 26 | -- } 27 | -- } 28 | -- }) 29 | return function(config) 30 | config = config or {} 31 | if type(config) == 'function' then 32 | config = config(win) 33 | end 34 | 35 | local args = config.args or {} 36 | local min_size = TYPE.str_to_pct(config.min_size) 37 | local min_size_is_pct = min_size and true or false 38 | min_size = min_size or tonumber(config.min_size) or 0 39 | min_size_is_pct = min_size_is_pct or (min_size > 0 and min_size < 1) 40 | 41 | local max_size = TYPE.str_to_pct(config.max_size) or 0 42 | local max_size_is_pct = min_size and true or false 43 | max_size = max_size or tonumber(config.max_size) or 0 44 | max_size_is_pct = max_size_is_pct or (max_size > 0 and max_size < 1) 45 | return { 46 | get = function (top, bottom, win) 47 | local min_size_t = min_size 48 | if min_size_is_pct then 49 | min_size_t = MATH_CEIL(win.height * min_size) 50 | end 51 | local max_size_t = max_size 52 | if max_size_is_pct then 53 | max_size_t = MATH_CEIL(win.height * max_size) 54 | end 55 | 56 | local scope = MINI_INDENTSCOPE.get_scope(win.cursor[1], win.cursor[2]+1, args) 57 | if not scope.body.top or not scope.body.bottom then 58 | return nil 59 | end 60 | local diff = scope.body.bottom - scope.body.top 61 | if (min_size_t > 0 and diff < min_size_t) or (max_size_t > 0 and diff > max_size_t) then 62 | return nil 63 | end 64 | -- increase each by 1 to show the owner scope lines 65 | return {scope.body.top-1, scope.body.bottom+1} 66 | end 67 | } 68 | end 69 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/snacks.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | 4 | local SNACKS_SCOPE = require('snacks.scope') 5 | 6 | -- configure through vimade.setup({ 7 | -- focus = { 8 | -- providers = { 9 | -- filetypes = { 10 | -- default = { 11 | -- snacks = { 12 | -- args = {} (plugin config to be merged) 13 | -- -- anything not included in min_size -> max_size gets dropped, allowing providers to chain 14 | -- max_size is the maximum number of lines allowed to return. If the limit is exceeded, nothing is returned, 15 | -- and the next provider is checked. Default is 0 (infinity). 16 | -- max_size = number [1-N] -> number of lines. 17 | -- number [0.00001-0.99999] or '10%' -> percentage of height 18 | -- 19 | -- min_size is the minimum number of lines allowed to return. If the limit is not reached, nothing is returned, 20 | -- and the next provider is checked. Default is 1. 21 | -- min_size = number [1-N] -> number of lines. 22 | -- number [0.00001-0.99999] or '10%' -> percentage of height 23 | -- } 24 | -- } 25 | -- } 26 | -- } 27 | -- } 28 | -- }) 29 | return function(config) 30 | config = config or {} 31 | if type(config) == 'function' then 32 | config = config(win) 33 | end 34 | 35 | local args = config.args or {} 36 | local min_size = TYPE.str_to_pct(config.min_size) 37 | local min_size_is_pct = min_size and true or false 38 | min_size = min_size or tonumber(config.min_size) or 0 39 | min_size_is_pct = min_size_is_pct or (min_size > 0 and min_size < 1) 40 | 41 | local max_size = TYPE.str_to_pct(config.max_size) or 0 42 | local max_size_is_pct = min_size and true or false 43 | max_size = max_size or tonumber(config.max_size) or 0 44 | max_size_is_pct = max_size_is_pct or (max_size > 0 and max_size < 1) 45 | return { 46 | get = function (top, bottom, win) 47 | local min_size_t = min_size 48 | if min_size_is_pct then 49 | min_size_t = MATH_CEIL(win.height * min_size) 50 | end 51 | local max_size_t = max_size 52 | if max_size_is_pct then 53 | max_size_t = MATH_CEIL(win.height * max_size) 54 | end 55 | 56 | local scope = SNACKS_SCOPE.get(TYPE.extend(args, { 57 | buf = win.bufnr, 58 | pos = win.cursor 59 | })) 60 | if scope.from == -1 or scope.to == -1 then 61 | return nil 62 | end 63 | local diff = scope.to - scope.from 64 | if (min_size_t > 0 and diff < min_size_t) or (max_size_t > 0 and diff > max_size_t) then 65 | return nil 66 | end 67 | -- increase each by 1 to show the owner scope lines 68 | return {scope.from, scope.to} 69 | end 70 | } 71 | end 72 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/static.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | local MATH_FLOOR = math.floor 4 | local MATH_MAX = math.max 5 | local MATH_MIN = math.min 6 | 7 | -- configure through vimade.setup({ 8 | -- focus = { 9 | -- providers = { 10 | -- filetypes = { 11 | -- default = { 12 | -- static = { 13 | -- size is the number of lines that will be returned. Default is 0.5 14 | -- size = number [1-N] -> number of lines. 15 | -- number [0.00001-0.99999] or '10%' -> percentage of height 16 | -- } 17 | -- } 18 | -- } 19 | -- } 20 | -- } 21 | -- }) 22 | return function(config) 23 | config = config or {} 24 | if type(config) == 'function' then 25 | config = config(win) 26 | end 27 | local size = TYPE.str_to_pct(config.size) 28 | local size_is_pct = size and true or false 29 | size = size or tonumber(config.size) or 0 30 | return { 31 | name = 'static', 32 | get = function (top, bottom, win) 33 | local size_t = size 34 | if size_is_pct then 35 | size_t = MATH_CEIL(win.height * size) 36 | end 37 | local above = win.cursor[1] - MATH_FLOOR(size_t / 2) 38 | local below = win.cursor[1] + MATH_CEIL(size_t / 2) - 1 -- (for current row) 39 | if above < top then 40 | below = below + (top - above) 41 | above = top 42 | end 43 | if below > bottom then 44 | above = above - (below - bottom) 45 | below = bottom 46 | end 47 | return {MATH_MAX(top, above), MATH_MIN(below, bottom)} 48 | end 49 | } 50 | end 51 | -------------------------------------------------------------------------------- /lua/vimade/focus/providers/treesitter.lua: -------------------------------------------------------------------------------- 1 | local TYPE = require('vimade.util.type') 2 | local MATH_CEIL = math.ceil 3 | 4 | -- configure through vimade.setup({ 5 | -- focus = { 6 | -- providers = { 7 | -- filetypes = { 8 | -- default = { 9 | -- treesitter = { 10 | -- -- anything not included in min_size -> max_size gets dropped, allowing providers to chain 11 | -- max_size is the maximum number of lines allowed to return. If the limit is exceeded, nothing is returned, 12 | -- and the next provider is checked. Default is 0 (infinity). 13 | -- max_size = number [1-N] -> number of lines. 14 | -- number [0.00001-0.99999] or '10%' -> percentage of height 15 | -- 16 | -- min_size is the minimum number of lines allowed to return. If the limit is not reached, nothing is returned, 17 | -- and the next provider is checked. Default is 1. 18 | -- min_size = number [1-N] -> number of lines. 19 | -- number [0.00001-0.99999] or '10%' -> percentage of height 20 | -- 21 | -- min_node_size is the minimum number of lines in a treesitter node to be considered valid. 22 | -- and the next provider is checked. Default is 2. 23 | -- min_node_size = number [1-N] -> number of lines. 24 | -- number [0.00001-0.99999] or '10%' -> percentage of height 25 | -- 26 | -- include is a list of node names that are considered valid. Anything outside this list is excluded. 27 | -- Default is nil. 28 | -- include = {'if_statement', 'block', ...} 29 | -- 30 | -- exclude is a list of node names that are excluded. Anything inside this list is excluded. 31 | -- Default is {'block'}. 32 | -- exclude = {'if_statement', 'block', ...} 33 | -- } 34 | -- } 35 | -- } 36 | -- } 37 | -- } 38 | -- }) 39 | return function(config) 40 | config = config or {} 41 | if type(config) == 'function' then 42 | config = config(win) 43 | end 44 | 45 | local min_size = TYPE.str_to_pct(config.min_size) 46 | local min_size_is_pct = min_size and true or false 47 | min_size = min_size or tonumber(config.min_size) or 1 48 | min_size_is_pct = min_size_is_pct or (min_size > 0 and min_size < 1) 49 | 50 | local max_size = TYPE.str_to_pct(config.max_size) 51 | local max_size_is_pct = max_size and true or false 52 | max_size = max_size or tonumber(config.max_size) or 0 53 | max_size_is_pct = max_size_is_pct or (max_size > 0 and max_size < 1) 54 | 55 | local min_node_size = TYPE.str_to_pct(config.min_node_size) 56 | local min_node_size_is_pct = min_node_size and true or false 57 | min_node_size = min_node_size or tonumber(config.min_node_size) or 2 58 | min_node_size_is_pct = min_node_size_is_pct or (min_node_size > 0 and min_node_size < 1) 59 | 60 | local include = config.include or nil 61 | local exclude = config.exclude or nil 62 | local include_map 63 | local exclude_map = {} 64 | 65 | if include then 66 | include_map = {} 67 | for _, name in ipairs(include) do 68 | include_map[name] = true 69 | end 70 | end 71 | 72 | if exclude then 73 | for _, name in ipairs(exclude) do 74 | exclude_map[name] = true 75 | end 76 | end 77 | 78 | return { 79 | get = function (top, bottom, win) 80 | local min_size_t = min_size 81 | if min_size_is_pct then 82 | min_size_t = MATH_CEIL(win.height * min_size) 83 | end 84 | local max_size_t = max_size 85 | if max_size_is_pct then 86 | max_size_t = MATH_CEIL(win.height * max_size) 87 | end 88 | local min_node_size_t = min_node_size 89 | if min_node_size_is_pct then 90 | min_node_size_t = MATH_CEIL(win.height * min_node_size) 91 | end 92 | 93 | local status, parser = pcall(vim.treesitter.get_parser, win.bufnr) 94 | if not status or not parser then 95 | return nil 96 | end 97 | pcall(parser.parse, parser, top, bottom) 98 | local status, node = pcall(vim.treesitter.get_node,{bufnr=win.bufnr, pos={win.cursor[1]-1,win.cursor[2]}}) 99 | 100 | if not status or not node then 101 | return nil 102 | end 103 | 104 | while ( 105 | node and ( 106 | exclude_map[node:type()] 107 | or (include_map and not include_map[node:type()]) 108 | or (min_node_size_t > 0 and (node:end_() - node:start()) < min_node_size_t) 109 | )) do 110 | node = node:parent() 111 | end 112 | if node then 113 | local result = {node:start() + 1, node:end_() + 1} 114 | local diff = result[2] - result[1] 115 | if (min_size_t > 0 and diff < min_size_t) or (max_size_t > 0 and diff > max_size_t) then 116 | return nil 117 | end 118 | return result 119 | end 120 | return nil 121 | end 122 | } 123 | end 124 | -------------------------------------------------------------------------------- /lua/vimade/highlighters/terminal.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.is_type = function(win) 4 | if win.terminal_match and not win.terminal then 5 | M.unhighlight(win) 6 | end 7 | return win.terminal 8 | end 9 | 10 | M.highlight = function(win) 11 | if not win.terminal_match then 12 | win.terminal_match = vim.fn.matchadd('Normal', '.*', 0, -1, {window = win.winid}) 13 | end 14 | end 15 | 16 | M.unhighlight = function(win) 17 | if win.terminal_match then 18 | vim.fn.matchdelete(win.terminal_match, win.winid) 19 | win.terminal_match = nil 20 | end 21 | end 22 | 23 | return M 24 | -------------------------------------------------------------------------------- /lua/vimade/init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local FADER = require('vimade.fader') 3 | 4 | if vim == nil or vim.api == nil then 5 | return 6 | end 7 | 8 | M.setup = function(config) 9 | return FADER.setup(config) 10 | end 11 | 12 | M.getInfo = function () 13 | return FADER.getInfo() 14 | end 15 | 16 | M.disable = function () 17 | FADER.disable() 18 | end 19 | 20 | M.redraw = function () 21 | FADER.unhighlightAll() 22 | vim.schedule(FADER.redraw) 23 | end 24 | 25 | M.update = function () 26 | FADER.tick(); 27 | end 28 | 29 | M.softInvalidateBuffer = function () 30 | -- no plans for implementation at this time (not needed) 31 | end 32 | 33 | M.softInvalidateSigns = function () 34 | -- no plans for implementation at this time (not needed) 35 | end 36 | 37 | M.animate = function () 38 | FADER.animate() 39 | end 40 | 41 | return M 42 | -------------------------------------------------------------------------------- /lua/vimade/recipe/default.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local ANIMATE = require('vimade.style.value.animate') 4 | local CONDITION = require('vimade.style.value.condition') 5 | local DIRECTION = require('vimade.style.value.direction') 6 | local GLOBALS 7 | local FADE = require('vimade.style.fade') 8 | local TINT = require('vimade.style.tint') 9 | local TYPE = require('vimade.util.type') 10 | local Component = require('vimade.style.component').Component 11 | local Invert = require('vimade.style.invert').Invert 12 | local Link = require('vimade.style.link').Link 13 | local Fade = FADE.Fade 14 | local Tint = TINT.Tint 15 | 16 | M.__init = function(args) 17 | GLOBALS = args.GLOBALS 18 | end 19 | 20 | 21 | local default_fade = FADE.Default().value() 22 | local default_tint = TINT.Default().value() 23 | 24 | local default = function (config) 25 | local animation = config.animate and { 26 | duration = config.duration, 27 | delay = config.delay, 28 | ease = config.ease, 29 | direction = config.direction, 30 | } or nil 31 | return { 32 | Component('Mark', { 33 | condition = CONDITION.IS_MARK, 34 | style = { 35 | Link({ 36 | condition = CONDITION.ALL, 37 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 38 | }), 39 | Tint({ 40 | condition = CONDITION.INACTIVE, 41 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 42 | to = default_tint, 43 | })) or default_tint 44 | }), 45 | Invert({ 46 | condition = CONDITION.ALL, 47 | value = 0.02, 48 | }) 49 | } 50 | }), 51 | Component('Focus', { 52 | condition = CONDITION.IS_FOCUS, 53 | style = { 54 | Link({ 55 | condition = CONDITION.ALL, 56 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 57 | }), 58 | Tint({ 59 | condition = CONDITION.INACTIVE, 60 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 61 | to = default_tint, 62 | })) or default_tint 63 | }), 64 | Fade({ 65 | condition = CONDITION.INACTIVE, 66 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 67 | to = default_fade, 68 | start = 1, 69 | })) or default_fade 70 | }) 71 | } 72 | }), 73 | Component('Pane', { 74 | condition = CONDITION.IS_PANE, 75 | style = { 76 | Tint({ 77 | condition = CONDITION.INACTIVE, 78 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 79 | to = default_tint, 80 | })) or default_tint 81 | }), 82 | Fade({ 83 | condition = CONDITION.INACTIVE_OR_FOCUS, 84 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 85 | to = default_fade, 86 | start = function (style, state) 87 | if GLOBALS.vimade_focus_active then 88 | return default_fade(style, state) 89 | else 90 | return 1 91 | end 92 | end, 93 | })) or default_fade 94 | }) 95 | } 96 | }) 97 | } 98 | end 99 | 100 | --@param config { 101 | -- @optional condition: CONDITION = CONDITION.INACTIVE 102 | -- @optional animate: boolean = false 103 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 104 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 105 | -- @optional duration: number = ANIMATE.DEFAULT_DURATION 106 | -- @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 107 | --} 108 | M.Default = function(config) 109 | config = TYPE.shallow_copy(config) 110 | return { 111 | style = default(config) 112 | } 113 | end 114 | 115 | return M 116 | -------------------------------------------------------------------------------- /lua/vimade/recipe/duo.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local ANIMATE = require('vimade.style.value.animate') 4 | local CONDITION = require('vimade.style.value.condition') 5 | local DIRECTION = require('vimade.style.value.direction') 6 | local FADE = require('vimade.style.fade') 7 | local TINT = require('vimade.style.tint') 8 | local TYPE = require('vimade.util.type') 9 | local GLOBALS = require('vimade.state.globals') 10 | local Component = require('vimade.style.component').Component 11 | local Invert = require('vimade.style.invert').Invert 12 | local Link = require('vimade.style.link').Link 13 | local Fade = FADE.Fade 14 | local Tint = TINT.Tint 15 | 16 | local default_tint = TINT.Default().value() 17 | local default_fade = FADE.Default().value() 18 | 19 | local duo_tint_to = function(config) 20 | return function (style, state) 21 | local to = style.resolve(default_tint, state) 22 | if to then 23 | local pct = config.window_pct 24 | if not (style.win.area_owner or style.win).nc then 25 | pct = 0 26 | elseif (style.win.area_owner or style.win).bufnr == GLOBALS.current.bufnr then 27 | pct = config.buffer_pct 28 | end 29 | for i, color in pairs(to) do 30 | if color.rgb then 31 | if color.intensity == nil then 32 | color.intensity = 0 33 | end 34 | color.intensity = color.intensity * pct 35 | end 36 | end 37 | end 38 | return to 39 | end 40 | end 41 | local duo_fade_to = function(config) 42 | return function (style, state) 43 | if not (style.win.area_owner or style.win).nc and not GLOBALS.vimade_focus_active then 44 | return 1 45 | end 46 | local to = style.resolve(fade_default, state) 47 | local pct = config.window_pct 48 | if to and (style.win.area_owner or style.win).bufnr == GLOBALS.current.bufnr then 49 | pct = config.buffer_pct 50 | end 51 | return to + (1 - to) * (1 - pct) 52 | end 53 | end 54 | local duo = function (config) 55 | local animation = config.animate and { 56 | duration = config.duration, 57 | delay = config.delay, 58 | ease = config.ease, 59 | direction = config.direction, 60 | } or nil 61 | return { 62 | Component('Mark', { 63 | condition = CONDITION.IS_MARK, 64 | style = { 65 | Link({ 66 | condition = CONDITION.ALL, 67 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 68 | }), 69 | Tint({ 70 | condition = CONDITION.INACTIVE, 71 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 72 | to = duo_tint_to(config), 73 | })) or duo_tint_to(config) 74 | }), 75 | Invert({ 76 | condition = CONDITION.ALL, 77 | value = 0.02, 78 | }) 79 | } 80 | }), 81 | Component('Focus', { 82 | condition = CONDITION.IS_FOCUS, 83 | style = { 84 | Link({ 85 | condition = CONDITION.ALL, 86 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 87 | }), 88 | Tint({ 89 | condition = CONDITION.INACTIVE, 90 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 91 | to = duo_tint_to(config), 92 | direction = DIRECTION.OUT, 93 | start = function(style, state) 94 | return style.win.area_owner.style_state.custom['pane-tint'].start 95 | or nil 96 | end 97 | })) or duo_tint_to(config) 98 | }), 99 | Fade({ 100 | condition = CONDITION.INACTIVE, 101 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 102 | to = duo_fade_to(config), 103 | direction = DIRECTION.OUT, 104 | start = function(style, state) 105 | return style.win.area_owner.style_state.custom['pane-fade'].value 106 | or 1 107 | end 108 | })) or duo_fade_to(config) 109 | }), 110 | } 111 | }), 112 | Component('Pane', { 113 | condition = CONDITION.IS_PANE, 114 | style = { 115 | TINT.Tint({ 116 | condition = CONDITION.INACTIVE, 117 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 118 | id = 'pane-tint', 119 | to = duo_tint_to(config), 120 | start = function(style, state) 121 | return state.start or nil 122 | end 123 | })) or duo_tint_to(config) 124 | }), 125 | FADE.Fade({ 126 | condition = CONDITION.INACTIVE_OR_FOCUS, 127 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 128 | id = 'pane-fade', 129 | to = duo_fade_to(config), 130 | start = function(style, state) 131 | return 132 | (GLOBALS.vimade_focus_active and duo_fade_to(config)(style,state)) 133 | or state.start 134 | or 1 135 | end 136 | })) or duo_fade_to(config), 137 | }) 138 | } 139 | }), 140 | } 141 | end 142 | 143 | --@param config { 144 | -- @optional buffer_pct: number[0-1] = 0.382 145 | -- @optional window_pct: number[0-1] = 1 146 | -- @optional animate: boolean = false 147 | -- @optional condition: CONDITION = CONDITION.INACTIVE 148 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 149 | -- @optional direction: DIRECTION = DIRECTION.IN_OUT 150 | -- @optional duration: number = ANIMATE.DEFAULT_DURATION 151 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 152 | -- @optional ncmode: 'windows'|'buffers' = 'windows' 153 | --} 154 | M.Duo = function(config) 155 | config = TYPE.shallow_copy(config) 156 | config.ncmode = config.ncmode or 'windows' 157 | config.buffer_pct = config.buffer_pct or 0.382 158 | config.window_pct = config.window_pct or 1 159 | config.direction = config.direction or DIRECTION.IN_OUT 160 | return { 161 | style = duo(config), 162 | ncmode = config.ncmode 163 | } 164 | end 165 | 166 | return M 167 | -------------------------------------------------------------------------------- /lua/vimade/recipe/minimalist.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = {} 3 | 4 | local ANIMATE = require('vimade.style.value.animate') 5 | local CONDITION = require('vimade.style.value.condition') 6 | local DIRECTION = require('vimade.style.value.direction') 7 | local GLOBALS = require('vimade.state.globals') 8 | local FADE = require('vimade.style.fade') 9 | local TINT = require('vimade.style.tint') 10 | local TYPE = require('vimade.util.type') 11 | local Include = require('vimade.style.include').Include 12 | local Invert = require('vimade.style.invert').Invert 13 | local Exclude = require('vimade.style.exclude').Exclude 14 | local Component = require('vimade.style.component').Component 15 | local Link = require('vimade.style.link').Link 16 | local Fade = FADE.Fade 17 | local Tint = TINT.Tint 18 | 19 | local EXCLUDE_NAMES = {'LineNr', 'LineNrBelow', 'LineNrAbove', 'WinSeparator', 'EndOfBuffer'} 20 | local NO_VISIBILITY_NAMES = {'LineNr', 'LineNrBelow', 'LineNrAbove', 'EndOfBuffer'} 21 | local LOW_VISIBILITY_NAMES = {'WinSeparator'} 22 | 23 | local default_fade = FADE.Default().value() 24 | local default_tint = TINT.Default().value() 25 | 26 | local create_unlink = function(names) 27 | local result = {} 28 | for _, name in ipairs(names) do 29 | table.insert(result, { 30 | from = name, 31 | to = nil 32 | }) 33 | end 34 | return result 35 | end 36 | 37 | local get_fade_style = function(config, animation) 38 | config.default.start = config.default.start or 1 39 | config.default.to = config.default.to or default_fade 40 | config.low_visibility.start = config.low_visibility.start or 1 41 | config.low_visibility.to = config.low_visibility.to or 0.1 42 | config.no_visibility.start = config.no_visibility.start or 1 43 | config.no_visibility.to = config.no_visibility.to or 0 44 | return { 45 | Exclude({ 46 | condition = config.condition, 47 | value = config.default.names, 48 | style = { 49 | Fade({ 50 | condition = config.condition, 51 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 52 | id = 'fade', 53 | reset = config.reset, 54 | direction = config.direction, 55 | to = config.default.to, 56 | start = config.default.start 57 | })) or config.default.to, 58 | }) 59 | } 60 | }), 61 | Include({ 62 | condition = config.condition, 63 | value = config.no_visibility.names, 64 | style = { 65 | Fade({ 66 | condition = config.condition, 67 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 68 | id = 'no-vis-fade', 69 | reset = config.reset, 70 | direction = config.direction, 71 | to = config.no_visibility.to, 72 | start = config.no_visibility.start, 73 | })) or config.no_visibility.to, 74 | }) 75 | } 76 | }), 77 | Include({ 78 | condition = config.condition, 79 | value = config.low_visibility.names, 80 | style = { 81 | Fade({ 82 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 83 | id = 'low-vis-fade', 84 | reset = config.reset, 85 | direction = config.direction, 86 | to = config.low_visibility.to, 87 | start = config.low_visibility.start, 88 | })) or config.low_visibility.to, 89 | }) 90 | } 91 | }) 92 | } 93 | end 94 | 95 | local minimalist = function (config) 96 | local animation = config.animate and { 97 | duration = config.duration, 98 | delay = config.delay, 99 | ease = config.ease, 100 | direction = config.direction, 101 | } or nil 102 | local result = { 103 | Component('Mark', { 104 | condition = CONDITION.IS_MARK, 105 | style = TYPE.style_concat({ 106 | Link({ 107 | condition = CONDITION.ALL, 108 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 109 | }), 110 | Link({ 111 | condition = CONDITION.INACTIVE, 112 | value = create_unlink(config.exclude_names), 113 | }), 114 | TINT.Tint({ 115 | condition = CONDITION.INACTIVE, 116 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 117 | to = default_tint 118 | })) or default_tint 119 | }) 120 | }, get_fade_style({ 121 | condition = CONDITION.INACTIVE, 122 | default = {names = config.exclude_names, to = 1}, 123 | low_visibility = {names = config.low_visibility_names, to = 1}, 124 | no_visibility = {names = config.no_visibility_names, to = 0} 125 | }, animation), { 126 | Invert({ 127 | condition = CONDITION.ALL, 128 | value = 0.02 129 | }) 130 | }) 131 | }), 132 | Component('Focus', { 133 | condition = CONDITION.IS_FOCUS, 134 | style = TYPE.style_concat({ 135 | Link({ 136 | condition = CONDITION.ALL, 137 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 138 | }), 139 | Link({ 140 | condition = CONDITION.INACTIVE, 141 | value = create_unlink(config.exclude_names), 142 | }), 143 | TINT.Tint({ 144 | condition = CONDITION.INACTIVE, 145 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 146 | to = default_tint 147 | })) or default_tint 148 | }) 149 | }, get_fade_style(TYPE.extend({}, config, { 150 | condition = CONDITION.INACTIVE, 151 | default = {names = config.exclude_names, to = default_fade}, 152 | low_visibility = {names = config.low_visibility_names, to = config.low_visibility_fadelevel}, 153 | no_visibility = {names = config.no_visibility_names, to = config.no_visibility_fadelevel or 0}, 154 | }), animation)) 155 | }), 156 | Component('Pane', { 157 | condition = CONDITION.IS_PANE, 158 | style = TYPE.style_concat({ 159 | Link({ 160 | condition = CONDITION.INACTIVE_OR_FOCUS, 161 | value = create_unlink(config.exclude_names), 162 | }), 163 | TINT.Tint({ 164 | condition = CONDITION.INACTIVE, 165 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 166 | to = default_tint 167 | })) or default_tint 168 | }) 169 | }, get_fade_style(TYPE.extend({}, config, { 170 | condition = CONDITION.INACTIVE_OR_FOCUS, 171 | default = { 172 | names = config.exclude_names, 173 | start = function (style, state) 174 | if GLOBALS.vimade_focus_active then 175 | return default_fade(style, state) 176 | else 177 | return 1 178 | end 179 | end, 180 | to = default_fade, 181 | }, 182 | low_visibility = { 183 | names = config.low_visibility_names, 184 | start = 1, 185 | to = function(style, state) 186 | return style.win.nc and 187 | (type(config.low_visibility_fadelevel) == 'function' and config.low_visibility_fadelevel(style, state) or config.low_visibility_fadelevel) 188 | or default_fade(style, state) 189 | end 190 | }, 191 | no_visibility = { 192 | names = config.no_visibility_names, 193 | start = 1, 194 | to = function(style, state) 195 | return style.win.nc and 196 | (type(config.no_visibility_fadelevel) == 'function' and config.no_visibility_fadelevel(style, state) or 0) 197 | or default_fade(style, state) 198 | end 199 | } 200 | }), animation)) 201 | }) 202 | 203 | } 204 | return result 205 | end 206 | 207 | --@param config { 208 | -- @optional condition: CONDITION = CONDITION.INACTIVE 209 | -- @optional exclude_names: {string...} = {'LineNr', 'LineNrBelow', 'LineNrAbove', 'WinSeparator', 'EndOfBuffer'} 210 | -- @optional no_visibility_names: {string...} = {'LineNr', 'LineNrBelow', 'LineNrAbove', 'EndOfBuffer'} 211 | -- @optional low_visibility_names: {string...} = {'WinSeparator'} 212 | -- @optional low_visibility_fadelevel: number = 0.1 213 | -- @optional animate: boolean = false 214 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 215 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 216 | -- @optional duration: number = ANIMATE.DEFAULT_DURATION 217 | -- @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 218 | --} 219 | M.Minimalist = function(config) 220 | config = TYPE.shallow_copy(config) 221 | config.condition = config.condition or CONDITION.INACTIVE 222 | config.exclude_names = config.exclude_names or EXCLUDE_NAMES 223 | config.no_visibility_names = config.no_visibility_names or NO_VISIBILITY_NAMES 224 | config.low_visibility_names = config.low_visibility_names or LOW_VISIBILITY_NAMES 225 | config.low_visibility_fadelevel = config.low_visibility_fadelevel or 0.1 226 | 227 | return { 228 | style = minimalist(config) 229 | } 230 | end 231 | 232 | return M 233 | -------------------------------------------------------------------------------- /lua/vimade/recipe/paper.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local ANIMATE = require('vimade.style.value.animate') 4 | local FADE = require('vimade.style.fade') 5 | local TINT = require('vimade.style.tint') 6 | local TYPE = require('vimade.util.type') 7 | 8 | local animate_paper = function (config) 9 | local animation = { 10 | duration = config.duration, 11 | delay = config.delay, 12 | ease = config.ease, 13 | direction = config.direction, 14 | } 15 | return { 16 | TINT.Tint({ 17 | condition = config.condition, 18 | value = ANIMATE.Tint(TYPE.extend({}, animation, { 19 | to = { 20 | fg = { 21 | rgb = {0,0,0}, 22 | intensity = 1 23 | }, 24 | bg = { 25 | rgb = {255,255,255}, 26 | intensity = 1 27 | }, 28 | }, 29 | })) 30 | }), 31 | FADE.Fade({ 32 | condition = config.condition, 33 | value = ANIMATE.Number(TYPE.extend({}, animation, { 34 | to = FADE.Default().value(), 35 | start = 1, 36 | })) 37 | }) 38 | } 39 | end 40 | 41 | local paper = function(config) 42 | return { 43 | TINT.Tint({ 44 | condition = config.condition, 45 | value = { 46 | fg = { 47 | rgb = {0,0,0}, 48 | intensity = 1 49 | }, 50 | bg = { 51 | rgb = {255,255,255}, 52 | intensity = 1 53 | }, 54 | }, 55 | }), 56 | FADE.Fade({ 57 | condition = config.condition, 58 | value = FADE.Default().value() 59 | }) 60 | } 61 | end 62 | 63 | --@param config { 64 | -- @optional condition: CONDITION = CONDITION.INACTIVE 65 | -- @optional animate: boolean = false 66 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 67 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 68 | -- @optional duration: number = ANIMATE.DEFAULT_DURATION 69 | -- @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 70 | --} 71 | M.Paper = function(config) 72 | config = TYPE.shallow_copy(config) 73 | return { 74 | style = config.animate and animate_paper(config) or paper(config) 75 | } 76 | end 77 | 78 | return M 79 | -------------------------------------------------------------------------------- /lua/vimade/recipe/paradox.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local ANIMATE = require('vimade.style.value.animate') 4 | local CONDITION = require('vimade.style.value.condition') 5 | local DIRECTION = require('vimade.style.value.direction') 6 | local FADE = require('vimade.style.fade') 7 | local GLOBALS = require('vimade.state.globals') 8 | local INVERT = require('vimade.style.invert') 9 | local TINT = require('vimade.style.tint') 10 | local TYPE = require('vimade.util.type') 11 | local Component = require('vimade.style.component').Component 12 | local Link = require('vimade.style.link').Link 13 | local Invert = INVERT.Invert 14 | local Fade = FADE.Fade 15 | local Tint = TINT.Tint 16 | 17 | local default_fade = FADE.Default().value() 18 | local default_tint = TINT.Default().value() 19 | 20 | local paradox = function (config) 21 | local invert_condition = function(style) 22 | if config.invert.active and (not style.win.terminal and not (style.win.area_owner or style.win).nc) then 23 | return true 24 | elseif not config.invert.active and (style.win.area_owner or style.win).nc then 25 | return true 26 | end 27 | return false 28 | end 29 | 30 | local animation = config.animate and { 31 | duration = config.duration, 32 | delay = config.delay, 33 | ease = config.ease, 34 | direction = config.direction, 35 | } or nil 36 | return { 37 | Component('Mark', { 38 | condition = CONDITION.IS_MARK, 39 | style = { 40 | Link({ 41 | condition = CONDITION.ALL, 42 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 43 | }), 44 | Tint({ 45 | condition = CONDITION.INACTIVE, 46 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 47 | to = default_tint, 48 | })) or default_tint 49 | }), 50 | Invert({ 51 | condition = config.invert.active and CONDITION.ACTIVE or CONDITION.INACTIVE, 52 | value = config.invert.to 53 | }), 54 | Invert({ 55 | condition = CONDITION.ALL, 56 | value = 0.02, 57 | }), 58 | } 59 | }), 60 | Component('Focus', { 61 | condition = CONDITION.IS_FOCUS, 62 | style = { 63 | Link({ 64 | condition = CONDITION.ALL, 65 | value = {{from='NormalFloat', to='Normal'}, {from='NormalNC', to='Normal'}} 66 | }), 67 | Tint({ 68 | condition = CONDITION.INACTIVE, 69 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 70 | to = default_tint, 71 | })) or default_tint 72 | }), 73 | Fade({ 74 | condition = CONDITION.INACTIVE, 75 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 76 | to = default_fade, 77 | start = 1, 78 | })) or default_fade 79 | }), 80 | Invert({ 81 | condition = invert_condition, 82 | value = animation and ANIMATE.Invert(TYPE.extend({}, animation, { 83 | to = config.invert.focus_to, 84 | start = config.invert.start, 85 | duration = config.invert.duration, 86 | direction = config.invert.direction, 87 | })) or config.invert.focus_to 88 | }) 89 | } 90 | }), 91 | Component('Pane', { 92 | condition = CONDITION.IS_PANE, 93 | style = { 94 | Tint({ 95 | condition = CONDITION.INACTIVE, 96 | value = animation and ANIMATE.Tint(TYPE.extend({}, animation, { 97 | to = default_tint, 98 | })) or default_tint 99 | }), 100 | Fade({ 101 | condition = CONDITION.INACTIVE_OR_FOCUS, 102 | value = animation and ANIMATE.Number(TYPE.extend({}, animation, { 103 | to = FADE.Default().value(), 104 | start = function (style, state) 105 | if GLOBALS.vimade_focus_active then 106 | return default_fade(style, state) 107 | else 108 | return 1 109 | end 110 | end, 111 | })) or default_fade 112 | }), 113 | Invert({ 114 | condition = invert_condition, 115 | value = animation and ANIMATE.Invert(TYPE.extend({}, animation, { 116 | to = config.invert.to, 117 | start = config.invert.start, 118 | duration = config.invert.duration, 119 | direction = config.invert.direction, 120 | })) or config.invert.to 121 | }), 122 | } 123 | }) 124 | } 125 | end 126 | 127 | --@param config { 128 | -- @optional invert = { 129 | -- @otional start = 0.15 130 | -- @otional to = 0.1 131 | -- @otional direction = DIRECTION.IN 132 | -- @otional duration = DEFAULT_DURATION 133 | -- @optional active = true (inverts the active window) 134 | -- } 135 | -- @optional condition: CONDITION = CONDITION.INACTIVE 136 | -- @optional animate: boolean = false 137 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 138 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 139 | -- @optional duration: number = ANIMATE.DEFAULT_DURATION 140 | -- @optional direction: DIRECTION = ANIMATE.DEFAULT_DIRECTION 141 | --} 142 | M.Paradox = function(config) 143 | config = TYPE.shallow_copy(config) 144 | config.invert = config.invert or {} 145 | config.invert.start = config.invert.start or 0.15 146 | config.invert.to = config.invert.to or 0.08 147 | config.invert.focus_to = config.invert.focus_to or 0.08 148 | config.invert.active = config.invert.active == nil and true or config.invert.active 149 | config.invert.direction = config.invert.direction and config.invert.direction or (config.invert.active and DIRECTION.IN or DIRECTION.OUT) 150 | return { 151 | style = paradox(config), 152 | ncmode = 'windows', 153 | } 154 | end 155 | 156 | return M 157 | -------------------------------------------------------------------------------- /lua/vimade/recipe/ripple.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local Include = require('vimade.style.include').Include 4 | local Exclude = require('vimade.style.exclude').Exclude 5 | local ANIMATE = require('vimade.style.value.animate') 6 | local DIRECTION = require('vimade.style.value.direction') 7 | local EASE = require('vimade.style.value.ease') 8 | local FADE = require('vimade.style.fade') 9 | local TINT = require('vimade.style.tint') 10 | local TYPE = require('vimade.util.type') 11 | local GLOBALS = require('vimade.state.globals') 12 | 13 | local get_win_infos = function () 14 | local distance = function (a1, a2, b1, b2) 15 | return math.sqrt(math.pow(a1-b1, 2) + math.pow(a2-b2, 2)) 16 | end 17 | local distance_between = function (info_a, info_b) 18 | local left_a = info_a.wincol * 0.75 19 | local left_b = info_b.wincol * 0.75 20 | local right_a = info_a.wincol + info_a.width * 0.75 21 | local right_b = info_b.wincol + info_b.width * 0.75 22 | local top_a = info_a.winrow 23 | local top_b = info_b.winrow 24 | local bottom_a = info_a.winrow + info_a.height 25 | local bottom_b = info_b.winrow + info_b.height 26 | if (bottom_a < top_b) and (right_b < left_a) then 27 | return distance(left_a, bottom_a, right_b, top_b) 28 | elseif (right_b < left_a) and (bottom_b < top_a) then 29 | return distance(left_a, top_a, right_b, bottom_b) 30 | elseif (bottom_b < top_a) and (right_a < left_b) then 31 | return distance(right_a, top_a, left_b, bottom_b) 32 | elseif (right_a < left_b) and (bottom_a < top_b) then 33 | return distance(right_a, bottom_a, left_b, top_b) 34 | elseif (right_b < left_a) then 35 | return left_a - right_b 36 | elseif (right_a < left_b) then 37 | return left_b - right_a 38 | elseif (bottom_b < top_a) then 39 | return top_a - bottom_b 40 | elseif (bottom_a < top_b) then 41 | return top_b - bottom_a 42 | else 43 | return 0 44 | end 45 | end 46 | local wininfo = vim.fn.getwininfo() 47 | local current_win = GLOBALS.current.winid 48 | local found_cur = nil 49 | for i, info in ipairs(wininfo) do 50 | if info.winid == current_win then 51 | found_cur = info 52 | break 53 | end 54 | end 55 | local result = {} 56 | for i, info in ipairs(wininfo) do 57 | if GLOBALS.current.tabnr == info.tabnr then 58 | result[info.winid] = { 59 | info = info, 60 | dist = distance_between(info, found_cur), 61 | area = info.width * info.height 62 | } 63 | end 64 | end 65 | return result 66 | end 67 | 68 | local win_infos = nil 69 | local max_distance = nil 70 | local max_area = nil 71 | local ripple_tick = function() 72 | win_infos = get_win_infos() 73 | max_distance = 0 74 | max_area = 0 75 | for winid, info in pairs(win_infos) do 76 | max_distance = math.max(info.dist, max_distance) 77 | max_area = math.max(info.area, max_area) 78 | end 79 | end 80 | local ripple_to_tint = function (style, state) 81 | local to = style.resolve(TINT.Default().value(), state) 82 | local m_dist = max_distance 83 | if m_dist == 0 then 84 | m_dist = 1 85 | end 86 | if to then 87 | for i, color in pairs(to) do 88 | if color.rgb then 89 | if color.intensity == nil then 90 | color.intensity = 1 91 | end 92 | color.intensity = (win_infos[style.win.winid].dist / m_dist) * color.intensity 93 | end 94 | end 95 | end 96 | return to 97 | end 98 | local ripple_to_fade = function (style, state) 99 | local to = style.resolve(FADE.Default().value(), state) 100 | local m_dist = max_distance 101 | if m_dist == 0 then 102 | m_dist = 1 103 | end 104 | return to + (1-win_infos[style.win.winid].dist / m_dist) * ((1-to) * 0.5) 105 | end 106 | local ripple_duration = function(style, state) 107 | local m_dist = max_distance 108 | if m_dist == 0 then 109 | return 0 110 | end 111 | return 100 + (win_infos[style.win.winid].dist / m_dist) * 200 112 | end 113 | local ripple_delay = function(style, state) 114 | local m_dist = max_distance 115 | if m_dist == 0 then 116 | return 0 117 | end 118 | return (win_infos[style.win.winid].dist / m_dist) * 300 119 | end 120 | local animate_ripple = function (config) 121 | local result = { 122 | TINT.Tint({ 123 | tick = ripple_tick, 124 | condition = config.condition, 125 | value = ANIMATE.Tint({ 126 | start = function (style, state) 127 | local start = style.resolve(TINT.Default().value(), state) 128 | if start then 129 | for i, color in pairs(start) do 130 | color.intensity = 0 131 | end 132 | end 133 | return start 134 | end, 135 | to = ripple_to_tint, 136 | direction = config.direction, 137 | duration = config.duration, 138 | delay = config.delay, 139 | ease = config.ease, 140 | }) 141 | }), 142 | FADE.Fade({ 143 | condition = config.condition, 144 | value = ANIMATE.Number({ 145 | to = ripple_to_fade, 146 | start = 1, 147 | direction = config.direction, 148 | duration = config.duration, 149 | delay = config.delay, 150 | ease = config.ease, 151 | }), 152 | }) 153 | } 154 | return result 155 | end 156 | 157 | local ripple = function (config) 158 | return { 159 | TINT.Tint({ 160 | tick = ripple_tick, 161 | condition = config.condition, 162 | value = ripple_to_tint, 163 | }), 164 | FADE.Fade({ 165 | condition = config.condition, 166 | value = ripple_to_fade, 167 | }), 168 | } 169 | end 170 | 171 | --@param config { 172 | -- @optional animate: boolean = false 173 | -- @optional condition: CONDITION = CONDITION.INACTIVE 174 | -- @optional delay: number = function_gradual_based_on_dist 175 | -- @optional direction: DIRECTION = DIRECTION.IN_OUT 176 | -- @optional duration: number = function_gradual_based_on_dist 177 | -- @optional ease: EASE = EASE.LINEAR 178 | -- @optional ncmode: 'windows'|'buffers' = 'windows' 179 | --} 180 | M.Ripple = function(config) 181 | config = TYPE.shallow_copy(config) 182 | config.direction = config.direction or DIRECTION.IN_OUT 183 | config.delay = config.delay or ripple_delay 184 | config.duration = config.duration or ripple_duration 185 | config.ease = config.ease or EASE.LINEAR 186 | config.ncmode = config.ncmode or 'windows' 187 | return { 188 | style = config.animate and animate_ripple(config) or ripple(config), 189 | ncmode = config.ncmode 190 | } 191 | end 192 | 193 | return M 194 | -------------------------------------------------------------------------------- /lua/vimade/recipe/space.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local ANIMATE = require('vimade.style.value.animate') 4 | local CONDITION = require('vimade.style.value.condition') 5 | local DIRECTION = require('vimade.style.value.direction') 6 | local EASE = require('vimade.style.value.ease') 7 | local FADE = require('vimade.style.fade') 8 | local TINT = require('vimade.style.tint') 9 | local TYPE = require('vimade.util.type') 10 | 11 | local animate_space = function (config) 12 | local animation = { 13 | duration = config.duration, 14 | delay = config.delay, 15 | ease = config.ease, 16 | direction = config.direction, 17 | } 18 | return { 19 | TINT.Tint({ 20 | condition = CONDITION.INACTIVE, 21 | value = ANIMATE.Tint(TYPE.extend({}, animation, { 22 | direction = DIRECTION.OUT, 23 | start = { 24 | fg = { 25 | rgb = {192,0,255}, 26 | intensity = 0.7 27 | }, 28 | bg = { 29 | rgb = {10,0,40}, 30 | intensity = 1 31 | }, 32 | }, 33 | to = { 34 | fg = { 35 | rgb = {192,0,255}, 36 | intensity = 0.35 37 | }, 38 | bg = { 39 | rgb = {0,0,0}, 40 | intensity = 1 41 | }, 42 | }, 43 | })) 44 | }), 45 | TINT.Tint({ 46 | condition = CONDITION.ACTIVE, 47 | value = ANIMATE.Tint(TYPE.extend({}, animation, { 48 | direction = DIRECTION.IN, 49 | start = { 50 | fg = { 51 | rgb = {192,0,255}, 52 | intensity = 0.35 53 | }, 54 | bg = { 55 | rgb = {0,0,0}, 56 | intensity = 1 57 | }, 58 | }, 59 | to = { 60 | fg = { 61 | rgb = {0,0,0}, 62 | intensity = 0 63 | }, 64 | bg = { 65 | rgb = {10,0,10}, 66 | intensity = 1, 67 | }, 68 | }, 69 | })) 70 | }), 71 | FADE.Fade({ 72 | condition = CONDITION.INACTIVE, 73 | direction = DIRECTION.OUT, 74 | value = ANIMATE.Number(TYPE.extend({}, animation, { 75 | to = FADE.Default().value(), 76 | start = 1, 77 | })) 78 | }) 79 | } 80 | end 81 | 82 | local space = function() 83 | return { 84 | TINT.Tint({ 85 | condition = CONDITION.INACTIVE, 86 | value = { 87 | fg = { 88 | rgb = {192,0,255}, 89 | intensity = 0.35 90 | }, 91 | bg = { 92 | rgb = {10,0,10}, 93 | intensity = 1 94 | }, 95 | }, 96 | }), 97 | TINT.Tint({ 98 | condition = CONDITION.ACTIVE, 99 | value = { 100 | bg = { 101 | rgb = {10,0,10}, 102 | intensity = 1 103 | }, 104 | }, 105 | }), 106 | FADE.Fade({ 107 | condition = CONDITION.INACTIVE, 108 | value = FADE.Default().value() 109 | }) 110 | } 111 | end 112 | 113 | --@param config { 114 | -- @optional animate: boolean = false 115 | -- @optional ease: EASE = ANIMATE.DEFAULT_EASE 116 | -- @optional delay: number = ANIMATE.DEFAULT_DELAY 117 | -- @optional duration: number = 1500 118 | --} 119 | M.Space = function(config) 120 | config = TYPE.shallow_copy(config) 121 | config.duration = config.duration or 1000 122 | return { 123 | style = config.animate and animate_space(config) or space(config) 124 | } 125 | end 126 | 127 | return M 128 | -------------------------------------------------------------------------------- /lua/vimade/state/namespace.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local TYPE = require('vimade.util.type') 4 | local COMPAT = require('vimade.util.compat') 5 | local REAL_NAMESPACE = require('vimade.state.real_namespace') 6 | 7 | local TABLE_INSERT = table.insert 8 | local TABLE_REMOVE = table.remove 9 | 10 | -- we want a high enough number for good probability of collisions 11 | -- during an animation. 55 should provide more than enough for basic 12 | -- configurations. The animation itself needs to round time to nearest 13 | -- good value (~32ms) to ensure the cache hit-rate is high enough. We also 14 | -- perform float rounding to ensure that the cache rate is reasonably high. 15 | --- 16 | -- The worst case here are long duration animations and animations that 17 | -- are impossible to be deterministic. The ripple recipe is a presents 18 | -- this issue well, where the re-use rate is implicitly limited due to 19 | -- how the calculation scales infinitely based on distance. TODO we should 20 | -- ensure that ripple is bucketed. 21 | -- 22 | -- Why 50?: 23 | -- - ~14 per default settings for Default, Minimalist, and Paradox recipes. 24 | -- - ~Duo takes more than 50 (roughly 80, but we don't need to cache every frame 25 | -- -- invalidations and recaching will automatically adjust the performance) 26 | -- - Marks and focus will add TBD (TODO determine if 50 is sufficient, likely is) 27 | local PENDING_LIMIT = 50 28 | 29 | local key_lookup = {} 30 | local winid_lookup = {} 31 | local used_ns = {} 32 | local free_ns = {} 33 | local pending_free_ns = {} 34 | local pending_free_ns_length = 0 35 | 36 | 37 | local ids = 0 38 | local get_next_id = function () 39 | ids = ids + 1 40 | return ids 41 | end 42 | 43 | -- will_reuse should only be set if the calling window will 44 | -- for sure reuse the namespace 45 | M.clear_winid = function (winid, will_self_reuse) 46 | local current_win_ns = winid_lookup[winid] 47 | winid_lookup[winid] = nil 48 | if current_win_ns ~= nil then 49 | current_win_ns.windows[winid] = nil 50 | if next(current_win_ns.windows) == nil then 51 | TABLE_INSERT(pending_free_ns, current_win_ns) 52 | key_lookup[current_win_ns.key] = nil 53 | pending_free_ns_length = pending_free_ns_length + 1 54 | if pending_free_ns_length > PENDING_LIMIT then 55 | TABLE_INSERT(free_ns, TABLE_REMOVE(pending_free_ns, 1)) 56 | pending_free_ns_length = PENDING_LIMIT 57 | end 58 | end 59 | end 60 | end 61 | 62 | -- This discards and uncaches all namespaces managed by vimade. This is useful 63 | -- when the namespace reaches an unrecoverable state (e.g. switching between some 64 | -- colorschemes may break the namespace link state). 65 | M.discard_all = function() 66 | for winid, state in pairs(winid_lookup) do 67 | M.clear_winid(winid) 68 | end 69 | free_ns = {} 70 | pending_free_ns_length = 0 71 | pending_free_ns = {} 72 | end 73 | 74 | M.get_replacement = function (win, real_ns, hi_key, skip_create) 75 | local key = hi_key 76 | 77 | local ns = key_lookup[key] 78 | local current_win_ns = winid_lookup[win.winid] 79 | 80 | -- see if the ns was switched, if so we need to clear the win 81 | if ns == nil or (current_win_ns ~= nil and current_win_ns ~= ns) then 82 | M.clear_winid(win.winid, ns == nil) 83 | ns = key_lookup[key] 84 | if not ns then 85 | -- also check the pending_free_ns 86 | for i, pending_ns in ipairs(pending_free_ns) do 87 | if pending_ns.key == key then 88 | ns = pending_ns 89 | TABLE_REMOVE(pending_free_ns, i) 90 | ns.dupe = true 91 | pending_free_ns_length = pending_free_ns_length - 1 92 | key_lookup[pending_ns.key] = pending_ns 93 | break 94 | end 95 | end 96 | end 97 | end 98 | if ns == nil then 99 | if skip_create then 100 | return nil 101 | end 102 | ns = TABLE_REMOVE(free_ns) or { 103 | -- vimade_ns = vim.api.nvim_create_namespace('vimade_' .. get_next_id()), 104 | vimade_ns = vim.api.nvim_create_namespace(''), 105 | vimade_highlights = {}, 106 | } 107 | ns.key = key 108 | ns.real_ns =real_ns 109 | ns.windows = {} 110 | ns.modified = true 111 | ns.created = true 112 | 113 | key_lookup[key] = ns 114 | used_ns[ns.vimade_ns] = true 115 | end 116 | 117 | ns.windows[win.winid] = win 118 | winid_lookup[win.winid] = ns 119 | 120 | -- refresh highlights and changed state -- 121 | M.check_ns_modified(ns) 122 | return ns 123 | end 124 | 125 | M.check_ns_modified = function(ns) 126 | local real = REAL_NAMESPACE.refresh(ns.real_ns, false) 127 | if ns.created or real.modified or real.sync_id ~= ns.sync_id then 128 | ns.modified = true 129 | ns.created = false 130 | ns.sync_id = real.sync_id 131 | else 132 | -- if ns.dupe then 133 | -- print('skipped due to dupe', pending_free_ns_length) 134 | -- end 135 | ns.modified = false 136 | end 137 | ns.dupe = nil 138 | ns.real = real 139 | end 140 | 141 | M.is_vimade_ns = function (vimade_ns) 142 | return used_ns[vimade_ns] ~= nil 143 | end 144 | 145 | M.from_winid = function (winid) 146 | local result = winid_lookup[winid] 147 | return result and result.vimade_ns 148 | end 149 | 150 | return M 151 | -------------------------------------------------------------------------------- /lua/vimade/state/real_namespace.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local bit = require('bit') 4 | local BIT_BAND = bit.band 5 | 6 | local TYPE = require('vimade.util.type') 7 | local COMPAT = require('vimade.util.compat') 8 | local ANIMATOR = require('vimade.animator') 9 | local GLOBALS 10 | local FADER 11 | local TABLE_INSERT = table.insert 12 | 13 | local NVIM_GET_HL = COMPAT.nvim_get_hl 14 | local global_sync_id = 1 15 | 16 | M.__init = function (args) 17 | GLOBALS = args.GLOBALS 18 | FADER = args.FADER 19 | 20 | FADER.on('tick:before', function () 21 | -- logic below defers invalidations during an animation. If the namespace source 22 | -- changes, we'll recompute afterwards. 23 | if next(M.cache) ~= nil and not ANIMATOR.animating then 24 | M.pending_removal = M.cache 25 | M.cache = {} 26 | end 27 | end) 28 | end 29 | 30 | M.resolve_all_links = function (real_ns, real_highlights, is_global) 31 | local copy_ns 32 | if GLOBALS.termguicolors then 33 | copy_ns = TYPE.copy_hl_ns_gui 34 | else 35 | copy_ns = TYPE.copy_hl_ns_cterm 36 | end 37 | local globals = copy_ns(is_global and real_highlights or GLOBALS.global_highlights) 38 | local overrides = real_ns ~= 0 and copy_ns(real_highlights) or {} 39 | local output = {} 40 | local visited = {} 41 | 42 | local walk_links = function (start_name, start_hi, start_ns) 43 | local hi = start_hi 44 | local name = start_name 45 | local ns = start_ns 46 | local real_walked = {} 47 | local global_walked = {} 48 | 49 | local chain = {} 50 | -- functions to try and get fake_ns, fallback to global in a number of conditions 51 | -- this unfortunately works in scenarios where Neovim breaks, so its not an exact match. 52 | -- Neovim seems to break in many circular scenarios and just shows colors that are derived 53 | -- somewhere on the link tree but for no apparent visible reason. 54 | local mark_and_next = function (name) 55 | if ns == 0 then 56 | if global_walked[name] then 57 | return nil 58 | else 59 | global_walked[name] = true 60 | return globals[name] 61 | end 62 | else 63 | if real_walked[name] then 64 | if global_walked[name] then 65 | return nil 66 | else 67 | global_walked[name] = true 68 | return globals[name] 69 | end 70 | elseif overrides[name] then 71 | real_walked[name] = true 72 | return overrides[name] 73 | else 74 | real_walked[name] = true 75 | global_walked[name] = true 76 | return globals[name] 77 | end 78 | end 79 | end 80 | 81 | hi = mark_and_next(name) 82 | local circular = false 83 | while hi do 84 | TABLE_INSERT(chain, {hi= hi, name = name}) 85 | if not hi.link then 86 | break 87 | end 88 | local maybe_next_name = hi.link 89 | local maybe_next = mark_and_next(maybe_next_name) 90 | if maybe_next == nil then 91 | -- break early if we encounter a circular chain so that 92 | -- we have the last good highlight 93 | circular = true 94 | break 95 | end 96 | hi = maybe_next 97 | name = maybe_next_name 98 | -- if we've already visited this node we are done, break early 99 | -- and use the already processed hi 100 | local visited_hi = visited[name] 101 | if visited_hi then 102 | hi = visited_hi 103 | break 104 | end 105 | end 106 | 107 | if circular == true then 108 | -- for circular handling we try our "best". Get the "actual" color 109 | -- which is often wrong, fallback to global, and break the link 110 | -- this is not a scenario that Neovim even consistently supports as it 111 | -- returns color information inconsistently. 112 | local hi_conf = {name = name, link = false} 113 | hi = NVIM_GET_HL(real_ns, hi_conf) 114 | if hi.link or next(hi) == nil then 115 | hi = NVIM_GET_HL(0, hi_conf) 116 | end 117 | hi.link = nil 118 | end 119 | -- apply the final highlights to each of the linked nodes 120 | for i, linked in ipairs(chain) do 121 | -- Replace the link chain with the end highlight values. 122 | -- If it was circular, it was already disconnected 123 | -- (see hi.link = nil above). 124 | -- This allows us to have the actual displayed highlight settings at each 125 | -- node. If a user wants to disconnect a node, it should behave 126 | -- intuitively and transition from the real active value. 127 | local link = linked.hi.link 128 | local linked_hi = {} 129 | linked.hi = linked_hi 130 | visited[linked.name] = linked_hi 131 | for k, v in pairs(hi) do 132 | linked_hi[k] = v 133 | end 134 | linked_hi.link = link 135 | end 136 | return chain 137 | end 138 | 139 | local output = {} 140 | 141 | for name, override in pairs(overrides) do 142 | if override.link then 143 | local chain = walk_links(name, override, real_ns) 144 | for i, linked in pairs(chain) do 145 | local linked_hi = linked.hi 146 | if linked_hi.link or linked_hi.fg or linked_hi.bg or linked_hi.sp or linked_hi.ctermfg or linked_hi.ctermbg or linked_hi.blend then 147 | output[linked.name] = linked_hi 148 | end 149 | end 150 | -- TODO cleanup, this is ugly 151 | elseif override.link or override.fg or override.bg or override.sp or override.ctermfg or override.ctermbg or override.blend then 152 | output[name] = override 153 | end 154 | end 155 | 156 | for name, override in pairs(globals) do 157 | -- repeat for globals, but ensure we don't overwrite anything existing 158 | if override.link then 159 | local chain = walk_links(name, override, 0) 160 | for i, linked in pairs(chain) do 161 | local linked_name = linked.name 162 | local linked_hi = linked.hi 163 | -- TODO cleanup, this is ugly 164 | if not output[name] and (linked_hi.link or linked_hi.fg or linked_hi.bg or linked_hi.sp or linked_hi.ctermfg or linked_hi.ctermbg or linked_hi.blend) then 165 | output[linked_name] = linked_hi 166 | end 167 | end 168 | elseif not output[name] and (override.link or override.fg or override.bg or override.sp or override.ctermfg or override.ctermbg or override.blend) then 169 | output[name] = override 170 | end 171 | end 172 | if output.Normal then 173 | -- do not try and grab NormalNC -> Neovim will fail to lookup the correct colors for circular highlights 174 | -- only use link resolving logic above 175 | output.Normal.foreground = nil 176 | output.Normal.background = nil 177 | end 178 | if output.NormalNC then 179 | -- do not try and grab NormalNC -> Neovim will fail to lookup the correct colors for circular highlights 180 | -- only use link resolving logic above 181 | output.NormalNC.foreground = nil 182 | output.NormalNC.background = nil 183 | end 184 | return output 185 | end 186 | 187 | M.is_desync = function(ns) 188 | local global_ns = M.cache[0] or M.pending_removal[0] 189 | return ns.id ~= 0 and global_ns and ns.sync_id ~= global_ns.sync_id 190 | end 191 | 192 | M.pending_removal = {} 193 | M.cache = {} 194 | M.refresh = function (real_ns, is_global) 195 | local ns = M.cache[real_ns] 196 | local filter_ns 197 | local equal_ns 198 | if GLOBALS.termguicolors then 199 | filter_ns = TYPE.filter_ns_gui 200 | equal_ns = TYPE.equal_ns_gui 201 | else 202 | filter_ns = TYPE.filter_ns_cterm 203 | equal_ns = TYPE.equal_ns_cterm 204 | end 205 | if not ns then 206 | ns = {} 207 | local global_ns = M.cache[0] 208 | local last = M.pending_removal[real_ns] 209 | M.cache[real_ns] = ns 210 | ns.id = real_ns 211 | ns.sync_id = last and last.sync_id 212 | ns.highlights = NVIM_GET_HL(real_ns, {link = true}) 213 | filter_ns(ns.highlights) 214 | if BIT_BAND(GLOBALS.tick_state, GLOBALS.RECALCULATE) > 0 then 215 | ns.modified = true 216 | elseif real_ns ~= 0 and 217 | (global_ns and (global_ns.modified or ns.sync_id ~= global_ns.sync_id)) then 218 | ns.modified = true 219 | elseif last == nil then 220 | ns.modified = true 221 | elseif last ~= nil then 222 | if equal_ns(last.highlights, ns.highlights) == false then 223 | ns.modified = true 224 | end 225 | end 226 | if (not last or not last.complete_highlights) or ns.modified then 227 | ns.complete_highlights = M.resolve_all_links(real_ns, ns.highlights, is_global) 228 | -- lets us know when to resync non-global namespaces 229 | -- this is atypical behavior but can occur if the global namespace 230 | -- changes on a different tab, gets synchronized and then user 231 | -- switches to a tab with separate namespaces 232 | if is_global then 233 | global_sync_id = global_sync_id + 1 234 | end 235 | ns.sync_id = global_sync_id 236 | else 237 | ns.complete_highlights = last.complete_highlights 238 | end 239 | end 240 | return ns 241 | end 242 | 243 | return M 244 | -------------------------------------------------------------------------------- /lua/vimade/style/component.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local CONDITION = require('vimade.style.value.condition') 4 | 5 | -- components do nothing except act as a named condition allowing users to override things based on concept 6 | -- @param componentName: string 7 | -- @param config = { 8 | -- style = {Fade(0.4)} # style to run on all names that aren included 9 | --} 10 | M.Component = function(name, config) 11 | local result = {} 12 | local _condition = config.condition or CONDITION.INACTIVE 13 | result.name = name 14 | result.tick = config.tick 15 | result.attach = function (win) 16 | local condition = _condition 17 | local children = {} 18 | for i, s in ipairs(config.style) do 19 | table.insert(children, s.attach(win)) 20 | end 21 | local style = {} 22 | style.win = win 23 | style._condition = _condition 24 | style._animating = false 25 | style.before = function (win, state) 26 | if type(_condition) == 'function' then 27 | condition = _condition(style, state) 28 | end 29 | if condition == false then 30 | return 31 | end 32 | for i, s in ipairs(children) do 33 | s.before(win, state) 34 | end 35 | end 36 | style.key = function (win, state) 37 | -- components don't need their own keys since they are just proxies to their children 38 | if condition == false then 39 | return '' 40 | end 41 | local key = '' 42 | local s_active = 0 43 | for j, s in ipairs(children) do 44 | if j ~= 0 then 45 | key = key .. ',' 46 | end 47 | local s_key = s.key(win, state) 48 | key = key .. s_key 49 | if string.len(s_key) > 0 then 50 | s_active = s_active + 1 51 | end 52 | end 53 | if s_active == 0 then 54 | return '' 55 | end 56 | return key 57 | end 58 | style.modify = function (highlights, to_hl) 59 | if condition == false then 60 | return 61 | end 62 | for i, s in ipairs(children) do 63 | s.modify(highlights, to_hl) 64 | end 65 | end 66 | return style 67 | end 68 | -- value is not exposed here, no use-case currently 69 | return result 70 | end 71 | return M 72 | -------------------------------------------------------------------------------- /lua/vimade/style/exclude.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local CONDITION = require('vimade.style.value.condition') 4 | 5 | local exclude_key_reducer = require('vimade.util.key_reducer')() 6 | 7 | -- @param config = { 8 | -- value= {'Folded', 'VertSplit', 'Normal', ...}, # list of names that should be skipped on the style array 9 | -- style = {Fade(0.4)} # styles to run on all names that aren't excluded 10 | --} 11 | M.Exclude = function(config) 12 | local result = {} 13 | local _condition = config.condition or CONDITION.INACTIVE 14 | result.tick = config.tick 15 | result.attach = function (win) 16 | local names = config.value 17 | local condition = _condition 18 | local children = {} 19 | for i, s in ipairs(config.style) do 20 | table.insert(children, s.attach(win)) 21 | end 22 | local exclude = {} 23 | local exclude_key = '' 24 | local style = {} 25 | style.win = win 26 | style._condition = _condition 27 | style._animating = false 28 | style.before = function (win, state) 29 | if type(_condition) == 'function' then 30 | condition = _condition(style, state) 31 | end 32 | if condition == false then 33 | return 34 | end 35 | local input 36 | 37 | if type(names) == 'function' then 38 | input = names(win) 39 | else 40 | input = names 41 | end 42 | if type(input) == 'string' then 43 | input = {input} 44 | end 45 | 46 | exclude_key = exclude_key_reducer.reduce_ipairs(input) 47 | 48 | exclude = {} 49 | for i, name in ipairs(input) do 50 | exclude[name] = true 51 | end 52 | 53 | for i, s in ipairs(children) do 54 | s.before(win, state) 55 | end 56 | end 57 | style.key = function (win, state) 58 | if condition == false then 59 | return '' 60 | end 61 | local key ='E-' .. exclude_key .. '(' 62 | local s_active = 0 63 | for j, s in ipairs(children) do 64 | if j ~= 0 then 65 | key = key .. ',' 66 | end 67 | local s_key = s.key(win, state) 68 | key = key .. s_key 69 | if string.len(s_key) > 0 then 70 | s_active = s_active + 1 71 | end 72 | end 73 | if s_active == 0 then 74 | return '' 75 | end 76 | key = key .. ')' 77 | return key 78 | end 79 | style.modify = function (highlights, to_hl) 80 | if condition == false then 81 | return 82 | end 83 | local excluded_for_children = {} 84 | for name, _ in pairs(exclude) do 85 | excluded_for_children[name] = highlights[name] 86 | highlights[name] = nil 87 | end 88 | for i, s in ipairs(children) do 89 | s.modify(highlights, to_hl) 90 | end 91 | for name, value in pairs(excluded_for_children) do 92 | highlights[name] = excluded_for_children[name] 93 | end 94 | end 95 | return style 96 | end 97 | -- value is not exposed here, no use-case currently 98 | return result 99 | end 100 | return M 101 | -------------------------------------------------------------------------------- /lua/vimade/style/fade.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local math = require('math') 4 | 5 | local CONDITION = require('vimade.style.value.condition') 6 | local COLOR_UTIL = require('vimade.util.color') 7 | local TYPE = require('vimade.util.type') 8 | local VALIDATE = require('vimade.util.validate') 9 | local GLOBALS 10 | 11 | local INTERPOLATE_LINEAR = COLOR_UTIL.interpolateLinear 12 | local INTERPOLATE_256 = COLOR_UTIL.interpolate256 13 | local INTERPOLATE_24B = COLOR_UTIL.interpolate24b 14 | local MATH_MAX = math.max 15 | local MATH_MIN = math.min 16 | 17 | M.__init = function(args) 18 | GLOBALS = args.GLOBALS 19 | end 20 | 21 | -- @param config { 22 | -- @required value = number | function (win) -> number -- number is the fadelevel that is applied to each window 23 | -- } 24 | M.Fade = function(config) 25 | local result = {} 26 | local _value = config.value 27 | local _condition = config.condition or CONDITION.INACTIVE 28 | result.tick = config.tick 29 | result.attach = function (win) 30 | local fade = _value 31 | local condition = _condition 32 | local style = {} 33 | style.win = win 34 | style._condition = _condition 35 | style._animating = false 36 | style.resolve = function (value, state) 37 | return VALIDATE.fade(TYPE.resolve_all_fn(value, style, state)) 38 | end 39 | style.before = function (win, state) 40 | fade = style.resolve(_value, state) 41 | -- condition needs to be the last check due to animation checks 42 | if type(_condition) == 'function' then 43 | condition = _condition(style, state) 44 | end 45 | end 46 | style.key = function (win, state) 47 | if condition == false then 48 | return '' 49 | end 50 | -- this function compounds the fadelevel only on the existing higlight 51 | -- only needs to be keyed by fadelevel 52 | return 'F-' .. fade 53 | end 54 | style.modify = function (highlights, to_hl) 55 | -- fade modifies all layers against the background 56 | -- skip links by default, use include to target them 57 | if condition == false or fade == nil then 58 | return 59 | end 60 | for _, hl in pairs(highlights) do 61 | if not hl.link then 62 | if to_hl.bg ~= nil then 63 | if hl.fg ~= nil then 64 | hl.fg = INTERPOLATE_24B(hl.fg, to_hl.bg, fade) 65 | end 66 | if hl.bg ~= nil then 67 | hl.bg = INTERPOLATE_24B(hl.bg, to_hl.bg, fade) 68 | end 69 | if hl.sp ~= nil then 70 | hl.sp = INTERPOLATE_24B(hl.sp, to_hl.bg, fade) 71 | end 72 | end 73 | if hl.blend ~= nil then 74 | --always assume blend is 100 75 | --some easing functions can go beyond 100 and under 0, this causes a neovim error 76 | --cap it here. 77 | hl.blend = MATH_MAX(0, MATH_MIN(100, INTERPOLATE_LINEAR(hl.blend, 100, fade))) 78 | end 79 | if to_hl.ctermbg ~= nil then 80 | if hl.ctermfg ~= nil then 81 | hl.ctermfg = INTERPOLATE_256(hl.ctermfg, to_hl.ctermbg, fade) 82 | end 83 | if hl.ctermbg ~= nil then 84 | hl.ctermbg = INTERPOLATE_256(hl.ctermbg, to_hl.ctermbg, fade) 85 | end 86 | end 87 | end 88 | end 89 | end 90 | return style 91 | end 92 | result.value = function (replacement) 93 | if replacement ~= nil then 94 | _value = replacement 95 | return result 96 | end 97 | return _value 98 | end 99 | return result 100 | end 101 | 102 | M.Default = function (config) 103 | return M.Fade(TYPE.extend({ 104 | condition = CONDITION.INACTIVE, 105 | value = function (style, state) 106 | local value = GLOBALS.fadelevel 107 | if type(value) == 'function' then 108 | value = value(style, state) 109 | end 110 | return value 111 | end 112 | }, config)) 113 | end 114 | 115 | return M 116 | -------------------------------------------------------------------------------- /lua/vimade/style/include.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local COMPAT = require('vimade.util.compat') 4 | local CONDITION = require('vimade.style.value.condition') 5 | local GLOBALS 6 | 7 | local include_key_reducer = require('vimade.util.key_reducer')() 8 | 9 | M.__init = function (args) 10 | GLOBALS = args.GLOBALS 11 | end 12 | 13 | -- @param config = { 14 | -- value= {'Folded', 'VertSplit', 'Normal', ...}, # list of names that should be skipped on the style array 15 | -- style = {Fade(0.4)} # style to run on all names that aren included 16 | --} 17 | M.Include = function(config) 18 | local result = {} 19 | local _condition = config.condition or CONDITION.INACTIVE 20 | result.tick = config.tick 21 | result.attach = function (win) 22 | local condition = _condition 23 | local names = config.value 24 | local children = {} 25 | for i, s in ipairs(config.style) do 26 | table.insert(children, s.attach(win)) 27 | end 28 | local include = {} 29 | local include_key = '' 30 | local style = {} 31 | style.win = win 32 | style._condition = _condition 33 | style._animating = false 34 | style.before = function (win, state) 35 | if type(_condition) == 'function' then 36 | condition = _condition(style, state) 37 | end 38 | if condition == false then 39 | return 40 | end 41 | local input 42 | 43 | if type(names) == 'function' then 44 | input = names(win) 45 | else 46 | input = names 47 | end 48 | if type(input) == 'string' then 49 | input = {input} 50 | end 51 | 52 | include_key = include_key_reducer.reduce_ipairs(input) 53 | 54 | include = {} 55 | for i, name in ipairs(input) do 56 | include[name] = true 57 | end 58 | 59 | for i, s in ipairs(children) do 60 | s.before(win, state) 61 | end 62 | end 63 | style.key = function (win, state) 64 | if condition == false then 65 | return '' 66 | end 67 | local key ='I-' .. include_key .. '(' 68 | local s_active = 0 69 | for j, s in ipairs(children) do 70 | if j ~= 0 then 71 | key = key .. ',' 72 | end 73 | local s_key = s.key(win, state) 74 | key = key .. s_key 75 | if string.len(s_key) > 0 then 76 | s_active = s_active + 1 77 | end 78 | end 79 | if s_active == 0 then 80 | return '' 81 | end 82 | key = key .. ')' 83 | return key 84 | end 85 | style.modify = function (highlights, to_hl) 86 | if condition == false then 87 | return 88 | end 89 | local highlights_for_children = {} 90 | for name, _ in pairs(include) do 91 | highlights_for_children[name] = highlights[name] 92 | end 93 | for i, s in ipairs(children) do 94 | s.modify(highlights_for_children, to_hl) 95 | end 96 | for name, value in pairs(highlights_for_children) do 97 | highlights[name] = value 98 | end 99 | end 100 | return style 101 | end 102 | -- value is not exposed here, no use-case currently 103 | return result 104 | end 105 | return M 106 | -------------------------------------------------------------------------------- /lua/vimade/style/invert.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local CONDITION = require('vimade.style.value.condition') 4 | local COLOR_UTIL = require('vimade.util.color') 5 | local TYPE = require('vimade.util.type') 6 | local VALIDATE = require('vimade.util.validate') 7 | 8 | local INTERPOLATE_256 = COLOR_UTIL.interpolate256 9 | local INTERPOLATE_24B = COLOR_UTIL.interpolate24b 10 | local TO_RGB = COLOR_UTIL.toRgb 11 | 12 | -- @required 13 | -- number | { 14 | -- @optional fg = 0-1, # applies the given intensity to text 15 | -- @optional bg = 0-1, # applies the given intensity to background 16 | -- @optional sp = 0-1, # applies the given intensity to special 17 | -- } 18 | M.Invert = function(config) 19 | local result = {} 20 | local _value = config.value 21 | local _condition = config.condition or CONDITION.INACTIVE 22 | result.tick = config.tick 23 | result.attach = function(win) 24 | local invert = _value 25 | local condition = _condition 26 | local style = {} 27 | style.win = win 28 | style._condition = _condition 29 | style._animating = false 30 | style.resolve = function (value, state) 31 | return VALIDATE.invert(TYPE.resolve_all_fn(value, style, state)) 32 | end 33 | style.before = function (win, state) 34 | invert = style.resolve(_value, state) 35 | -- condition needs to be the last check due to animation checks 36 | if type(_condition) == 'function' then 37 | condition = _condition(style, state) 38 | end 39 | end 40 | style.key = function (win, state) 41 | if condition == false then 42 | return '' 43 | end 44 | return 'INV-' .. (invert.fg or 0) .. '-' .. (invert.bg or 0) .. '-' .. (invert.sp or 0) 45 | end 46 | style.modify = function (highlights, to_hl) 47 | if condition == false or invert == nil then 48 | return 49 | end 50 | local color 51 | if to_hl.fg then 52 | to_hl.fg = INTERPOLATE_24B(to_hl.fg, 0xFFFFFF - to_hl.fg, 1 - invert.fg) 53 | end 54 | if to_hl.bg then 55 | to_hl.bg = INTERPOLATE_24B(to_hl.bg, 0xFFFFFF - to_hl.bg, 1 - invert.bg) 56 | end 57 | if to_hl.sp then 58 | to_hl.sp = INTERPOLATE_24B(to_hl.sp, 0xFFFFFF - to_hl.sp, 1 - invert.sp) 59 | end 60 | if to_hl.ctermfg then 61 | color = TO_RGB(to_hl.ctermfg, true) 62 | to_hl.ctermfg = INTERPOLATE_256(color, {255 - color[1], 255 - color[2], 255 - color[3]}, 1 - invert.fg) 63 | end 64 | if to_hl.ctermbg then 65 | color = TO_RGB(to_hl.ctermbg, true) 66 | to_hl.ctermbg = INTERPOLATE_256(color, {255 - color[1], 255 - color[2], 255 - color[3]}, 1 - invert.bg) 67 | end 68 | for _, hl in pairs(highlights) do 69 | if not hl.link then 70 | if hl.fg then 71 | hl.fg = INTERPOLATE_24B(hl.fg, 0xFFFFFF - hl.fg, 1 - invert.fg) 72 | end 73 | if hl.bg then 74 | hl.bg = INTERPOLATE_24B(hl.bg, 0xFFFFFF - hl.bg, 1 - invert.bg) 75 | end 76 | if hl.sp then 77 | hl.sp = INTERPOLATE_24B(hl.sp, 0xFFFFFF - hl.sp, 1 - invert.sp) 78 | end 79 | if hl.ctermfg then 80 | color = TO_RGB(hl.ctermfg, true) 81 | hl.ctermfg = INTERPOLATE_256(color, {255 - color[1], 255 - color[2], 255 - color[3]}, 1 - invert.fg) 82 | end 83 | if hl.ctermbg then 84 | color = TO_RGB(hl.ctermbg, true) 85 | hl.ctermbg = INTERPOLATE_256(color, {255 - color[1], 255 - color[2], 255 - color[3]}, 1 - invert.bg) 86 | end 87 | end 88 | end 89 | end 90 | return style 91 | end 92 | result.value = function (replacement) 93 | if replacement ~= nil then 94 | _value = replacement 95 | return result 96 | end 97 | return _value 98 | end 99 | return result 100 | end 101 | 102 | return M 103 | -------------------------------------------------------------------------------- /lua/vimade/style/link.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local CONDITION = require('vimade.style.value.condition') 4 | 5 | local TABLE_INSERT = table.insert 6 | 7 | local link_key_reducer = require('vimade.util.key_reducer')() 8 | 9 | -- @param config = { 10 | -- value= {{from='Folded'= , to={nil or 'LinkTarget'} ...}}, # list of names that should be linked or unlinked. 11 | --} 12 | M.Link = function(config) 13 | local result = {} 14 | local _condition = config.condition or CONDITION.INACTIVE 15 | result.tick = config.tick 16 | result.attach = function (win) 17 | local value = config.value 18 | local condition = _condition 19 | local link = {} 20 | local link_key = '' 21 | local style = {} 22 | style.win = win 23 | style._condition = _condition 24 | style._animating = false 25 | style.before = function (win, state) 26 | if type(_condition) == 'function' then 27 | condition = _condition(style, state) 28 | end 29 | if condition == false then 30 | return 31 | end 32 | 33 | local input 34 | if type(value) == 'function' then 35 | input = value(win) 36 | else 37 | input = value 38 | end 39 | 40 | link = {} 41 | local key_input = {} 42 | for _, v in ipairs(input) do 43 | TABLE_INSERT(key_input, v.from .. (v.to or '')) 44 | TABLE_INSERT(link, v) 45 | end 46 | link_key = link_key_reducer.reduce_ipairs(key_input) 47 | end 48 | style.key = function (win, state) 49 | if condition == false then 50 | return '' 51 | end 52 | return 'L-' .. link_key 53 | end 54 | style.modify = function (highlights, to_hl) 55 | if condition == false then 56 | return 57 | end 58 | local hl 59 | local to 60 | for _, li in ipairs(link) do 61 | hl = highlights[li.from] 62 | to = li.to 63 | if hl and to and to ~= '' then 64 | hl.link = to 65 | end 66 | end 67 | end 68 | return style 69 | end 70 | -- value is not exposed here, no use-case currently 71 | return result 72 | end 73 | return M 74 | -------------------------------------------------------------------------------- /lua/vimade/style/tint.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = {} 3 | 4 | local CONDITION = require('vimade.style.value.condition') 5 | local COLOR_UTIL = require('vimade.util.color') 6 | local TYPE = require('vimade.util.type') 7 | local VALIDATE = require('vimade.util.validate') 8 | local GLOBALS 9 | 10 | local DEEP_COPY = TYPE.deep_copy 11 | local TO_24B = COLOR_UTIL.to24b 12 | local TO_RGB = COLOR_UTIL.toRgb 13 | local INTERPOLATE_24B = COLOR_UTIL.interpolate24b 14 | local INTERPOLATE_256 = COLOR_UTIL.interpolate256 15 | 16 | M.__init = function (args) 17 | GLOBALS = args.GLOBALS 18 | end 19 | 20 | M._create_tint = function (tint) 21 | if type(tint) ~= 'table' then 22 | return nil 23 | end 24 | local fg = tint.fg 25 | local bg = tint.bg 26 | local sp = tint.sp 27 | if not bg and not fg and not sp then 28 | return nil 29 | end 30 | 31 | return { 32 | fg = fg and TO_24B(fg.rgb) or nil, 33 | ctermfg = fg and TO_RGB(fg.rgb) or nil, 34 | fg_intensity = fg and (1 - fg.intensity), 35 | bg = bg and TO_24B(bg.rgb) or nil, 36 | bg_intensity = bg and (1 - bg.intensity), 37 | ctermbg = bg and TO_RGB(bg.rgb) or nil, 38 | sp = sp and TO_24B(sp) or nil, 39 | sp_intensity = sp and (1 - sp.intensity), 40 | } 41 | end 42 | 43 | M.Tint = function(config) 44 | local result = {} 45 | local _value = config.value 46 | local _condition = config.condition or CONDITION.INACTIVE 47 | result.tick = config.tick 48 | result.attach = function(win) 49 | local tint = _value 50 | local to_hl = nil 51 | local condition = _condition 52 | local style = {} 53 | style.win = win 54 | style._condition = _condition 55 | style._animating = false 56 | style.resolve = function (value, state) 57 | return VALIDATE.tint(TYPE.resolve_all_fn(value, style, state)) 58 | end 59 | style.before = function (win, state) 60 | -- don't use style.resolve here for performance reasons 61 | tint = TYPE.resolve_all_fn(_value, style, state) 62 | if type(_condition) == 'function' then 63 | condition = _condition(style, state) 64 | end 65 | if condition == false then 66 | return 67 | end 68 | to_hl = M._create_tint(VALIDATE.tint(tint)) 69 | end 70 | style.key = function (win, state) 71 | if not to_hl or condition == false then 72 | return '' 73 | end 74 | return 'T-' 75 | .. (to_hl.fg and ((to_hl.fg or '') .. ',' .. (to_hl.ctermfg and ('t:' .. to_hl.ctermfg[1]..'-'..to_hl.ctermfg[2]..'-'..to_hl.ctermfg[3]) or '') .. ',' .. to_hl.fg_intensity) or '') .. '|' 76 | .. (to_hl.bg and ((to_hl.bg or '') .. ',' .. (to_hl.ctermbg and ('t:' .. to_hl.ctermbg[1]..'-'..to_hl.ctermbg[2]..'-'..to_hl.ctermbg[3]) or '') .. ',' .. to_hl.bg_intensity) or '') .. '|' 77 | .. (to_hl.sp and ((to_hl.sp or '') .. to_hl.sp_intensity) or '') 78 | end 79 | style.modify = function (highlights, target) 80 | if condition == false or not to_hl then 81 | return 82 | end 83 | if target.bg and to_hl.bg then 84 | target.bg = INTERPOLATE_24B(target.bg, to_hl.bg, to_hl.bg_intensity) 85 | end 86 | if target.ctermbg and to_hl.ctermbg then 87 | target.ctermbg = INTERPOLATE_256(target.ctermbg, to_hl.ctermbg, to_hl.bg_intensity) 88 | end 89 | for _, hl in pairs(highlights) do 90 | if not hl.link then 91 | if hl.fg and to_hl.fg then 92 | hl.fg = INTERPOLATE_24B(hl.fg, to_hl.fg, to_hl.fg_intensity) 93 | end 94 | if hl.sp and to_hl.sp then 95 | hl.sp = INTERPOLATE_24B(hl.sp, to_hl.sp, to_hl.sp_intensity) 96 | end 97 | if hl.ctermfg and to_hl.ctermfg then 98 | hl.ctermfg = INTERPOLATE_256(hl.ctermfg, to_hl.ctermfg, to_hl.fg_intensity) 99 | end 100 | if hl.bg and to_hl.bg then 101 | hl.bg = INTERPOLATE_24B(hl.bg, to_hl.bg, to_hl.bg_intensity) 102 | end 103 | if hl.ctermbg and to_hl.ctermbg then 104 | hl.ctermbg = INTERPOLATE_256(hl.ctermbg, to_hl.ctermbg, to_hl.bg_intensity) 105 | end 106 | end 107 | end 108 | end 109 | return style 110 | end 111 | result.value = function (replacement) 112 | if replacement ~= nil then 113 | _value = replacement 114 | return result 115 | end 116 | return _value 117 | end 118 | return result 119 | end 120 | 121 | M.Default = function (config) 122 | return M.Tint(TYPE.extend({ 123 | condition = CONDITION.INACTIVE, 124 | value = function (style, state) 125 | if type(GLOBALS.tint) == 'function' then 126 | return GLOBALS.tint(style, state) 127 | elseif type(GLOBALS.tint) == 'table' then 128 | return DEEP_COPY(GLOBALS.tint) 129 | end 130 | return GLOBALS.tint 131 | end 132 | }, config)) 133 | end 134 | 135 | return M 136 | -------------------------------------------------------------------------------- /lua/vimade/style/value/condition.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local GLOBALS 4 | 5 | M.__init = function (args) 6 | GLOBALS = args.GLOBALS 7 | end 8 | 9 | -- area_owner is the window owning a given area. 10 | M.ACTIVE = function (style, state) 11 | return ((style.win.area_owner or style.win).nc ~= true or style._animating) and true or false 12 | end 13 | M.INACTIVE = function (style, state) 14 | return ((style.win.area_owner or style.win).nc == true or style._animating) and true or false 15 | end 16 | M.FOCUS = function (style, state) 17 | return (((style.win.area_owner or style.win).nc ~= true and GLOBALS.vimade_focus_active) or style._animating) and true or false 18 | end 19 | M.INACTIVE_OR_FOCUS = function(style, state) 20 | return (((style.win.area_owner or style.win).nc == true or GLOBALS.vimade_focus_active) or style._animating) and true or false 21 | end 22 | 23 | M.ALL = function (style, state) 24 | return true 25 | end 26 | 27 | -- mark and focus are subtypes of area 28 | M.IS_MARK = function (style, state) 29 | return style.win.is_mark and true or false 30 | end 31 | M.IS_FOCUS = function (style, state) 32 | -- prevent terminal focus highlighting (loses colors) 33 | return (style.win.is_focus and not style.win.terminal) and true or false 34 | end 35 | M.IS_AREA = function(style, state) 36 | return style.win.area and true or false 37 | end 38 | -- pane represents anything that can be faded except areas 39 | M.IS_PANE = function (style, state) 40 | return (not style.win.area) and true or false 41 | end 42 | 43 | return M 44 | -------------------------------------------------------------------------------- /lua/vimade/style/value/direction.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.IN = 'in' 4 | M.OUT = 'out' 5 | M.IN_OUT = function (style, state) 6 | if (style.win.area_owner or style.win).nc == true then 7 | return M.OUT 8 | else 9 | return M.IN 10 | end 11 | end 12 | return M 13 | -------------------------------------------------------------------------------- /lua/vimade/style/value/ease.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.LINEAR = function (t) 4 | return t 5 | end 6 | M.IN_SINE = function (t) 7 | return 1 - math.cos((t * math.pi) / 2) 8 | end 9 | M.OUT_SINE = function (t) 10 | return math.sin((t * math.pi) / 2) 11 | end 12 | M.IN_OUT_SINE = function (t) 13 | return -(math.cos(math.pi*t) - 1) / 2 14 | end 15 | M.IN_QUAD = function (t) 16 | return t * t 17 | end 18 | M.OUT_QUAD = function (t) 19 | return 1 - (1 - t) * (1 - t) 20 | end 21 | M.IN_OUT_QUAD = function (t) 22 | if t < 0.5 then 23 | return 2 * t * t 24 | else 25 | return 1 - ((-2 * t + 2) ^ 2) / 2 26 | end 27 | end 28 | M.IN_CUBIC = function (t) 29 | return t * t * t 30 | end 31 | M.OUT_CUBIC = function (t) 32 | return 1 - (1 - t) ^ 3 33 | end 34 | M.IN_OUT_CUBIC = function (t) 35 | if t < 0.5 then 36 | return 4 * t * t * t 37 | else 38 | return 1 - ((-2 * t + 2) ^ 3) / 3 39 | end 40 | end 41 | M.IN_QUART = function (t) 42 | return t * t * t * t 43 | end 44 | M.OUT_QUART = function (t) 45 | return 1 - (1 - t) ^ 4 46 | end 47 | M.IN_OUT_QUART = function (t) 48 | if t < 0.5 then 49 | return 8 * t * t * t * t 50 | else 51 | return 1 - ((-2 * t + 2) ^ 4) / 2 52 | end 53 | end 54 | M.IN_EXPO = function (t) 55 | if t == 0 then 56 | return 0 57 | else 58 | return (2 ^ (10 * t - 10)) 59 | end 60 | end 61 | M.OUT_EXPO = function (t) 62 | if t == 1 then 63 | return 1 64 | else 65 | return 1 - (2 ^ (-10 * t)) 66 | end 67 | end 68 | M.IN_OUT_EXPO = function (t) 69 | if t == 0 or t == 1 then 70 | return t 71 | elseif t < 0.5 then 72 | return (2 ^ (20 * t - 10)) / 2 73 | else 74 | return (2 - (2 ^ (-20 * t + 10))) / 2 75 | end 76 | end 77 | M.IN_CIRC = function (t) 78 | return 1 - math.sqrt(1 - t ^ 2) 79 | end 80 | M.OUT_CIRC = function (t) 81 | return math.sqrt(1 - (t - 1) ^ 2) 82 | end 83 | M.IN_OUT_CIRC = function (t) 84 | if t < 0.5 then 85 | return (1 - math.sqrt(1 - (2 * t) ^ 2)) / 2 86 | else 87 | return (math.sqrt(1 - (-2 * t + 2) ^ 2) + 1) / 2 88 | end 89 | end 90 | M.IN_BACK = function (t) 91 | return 2.70158 * t * t * t - 1.70158 * t * t 92 | end 93 | M.OUT_BACK = function (t) 94 | return 1 + 2.70158 * ((t - 1) ^ 3) + 1.70158 * ((t - 1) ^ 2) 95 | end 96 | M.IN_OUT_BACK = function (t) 97 | local c1 = 1.70158; 98 | local c2 = c1 * 1.525; 99 | if t < 0.5 then 100 | return (((2 * t) ^ 2) * ((c2 + 1) * 2 * t - c2)) / 2 101 | else 102 | return (((2 * t - 2) ^ 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2; 103 | end 104 | end 105 | M.OUT_BOUNCE = function(t) 106 | if t < 1 / 2.75 then 107 | return 7.5625 * t * t 108 | elseif t < 2 / 2.75 then 109 | t = t - 1.5 / 2.75 110 | return 7.5625 * t * t + 0.75 111 | elseif t < 2.5 / 2.75 then 112 | t = t - 2.25 / 2.75 113 | return 7.5625 * t * t + 0.9375 114 | else 115 | t = t - 2.625 / 2.75 116 | return 7.5625 * t * t + 0.984375 117 | end 118 | end 119 | M.IN_BOUNCE = function(t) 120 | return 1 - M.OUT_BOUNCE(1 - t) 121 | end 122 | M.IN_OUT_BOUNCE = function(t) 123 | if t < 0.5 then 124 | return (1 - M.OUT_BOUNCE(1 - 2 * t)) / 2 125 | else 126 | return (1 + M.OUT_BOUNCE(2 * t - 1)) / 2 127 | end 128 | end 129 | 130 | return M 131 | -------------------------------------------------------------------------------- /lua/vimade/util/compat.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local FADER 4 | local nvim__needs_redraw = false 5 | -- best effort filled nvim__redraw (see tick:after below) 6 | M.nvim__redraw = vim.api.nvim__redraw or function () 7 | nvim__needs_redraw = true 8 | end 9 | 10 | M.__init = function(args) 11 | FADER = args.FADER 12 | if not vim.api.nvim__redraw then 13 | FADER.on('tick:after', function() 14 | nvim__needs_redraw = false 15 | vim.cmd('redraw') 16 | end) 17 | end 18 | end 19 | 20 | -- nvim_get_hl 21 | M.nvim_get_hl = 22 | -- default implementation + full support 23 | vim.api.nvim_get_hl 24 | -- fill previous implementations with missing attributes 25 | -- most notably bad attributes must be removed and 26 | -- 256 colors need to be filled. 27 | or (vim.api.nvim__get_hl_defs and 28 | function (ns, m) 29 | if m.id == nil and m.name == nil then 30 | -- missing id and name, then we should return all highlights 31 | local result = vim.api.nvim__get_hl_defs(0) 32 | for name, highlight in pairs(result) do 33 | for key, attr in pairs(highlight) do 34 | if key == true or key == 'true' then 35 | highlight[key] = nil 36 | elseif key == 'foreground' then 37 | highlight.fg = attr 38 | elseif key == 'background' then 39 | highlight.bg = attr 40 | end 41 | end 42 | local term = vim.api.nvim_get_hl_by_name(name, false) 43 | -- cterm colors are missing using get_hl_defs 44 | for key, attr in pairs(term) do 45 | if key == 'foreground' then 46 | highlight.ctermfg = attr 47 | elseif key == 'background' then 48 | highlight.ctermbg = attr 49 | end 50 | end 51 | end 52 | return result 53 | else 54 | local term 55 | local gui 56 | if m.id ~= nil then 57 | term = vim.api.nvim_get_hl_by_id(m.id, false) 58 | gui = vim.api.nvim_get_hl_by_id(m.id, true) 59 | else 60 | term = vim.api.nvim_get_hl_by_name(m.name, false) 61 | gui = vim.api.nvim_get_hl_by_name(m.name, true) 62 | end 63 | return { 64 | ctermfg = term.foreground, 65 | ctermbg = term.background, 66 | fg = gui.foreground, 67 | bg = gui.background, 68 | sp = gui.special, 69 | } 70 | end 71 | end) 72 | 73 | -- nvim_get_hl_ns may not exist in some versions, we can try 74 | -- and track the last set ns peformed via vimade. This may 75 | -- break behaviors if other plugins set the ns. 76 | if vim.api.nvim_get_hl_ns == nil then 77 | M.nvim_get_hl_ns = function (config) 78 | return vim.w[config.winid]._vimade_fallback_ns or 0 79 | end 80 | M.nvim_win_set_hl_ns = function (win, ns) 81 | vim.w[win]._vimade_fallback_ns = ns 82 | return vim.api.nvim_win_set_hl_ns(win, ns) 83 | end 84 | else 85 | -- versions with nvim_get_hl_ns should all behave as expected 86 | M.nvim_get_hl_ns = vim.api.nvim_get_hl_ns 87 | M.nvim_win_set_hl_ns = vim.api.nvim_win_set_hl_ns 88 | end 89 | 90 | return M 91 | -------------------------------------------------------------------------------- /lua/vimade/util/events.lua: -------------------------------------------------------------------------------- 1 | return function() 2 | local callbacks = {} 3 | local events = {} 4 | events.on = function (name, callback) 5 | if not callbacks[name] then 6 | callbacks[name] = {} 7 | end 8 | table.insert(callbacks[name], callback) 9 | end 10 | 11 | events.notify = function (name) 12 | local callbacks = callbacks[name] 13 | if callbacks then 14 | for k, callback in ipairs(callbacks) do 15 | callback() 16 | end 17 | end 18 | end 19 | return events 20 | end 21 | -------------------------------------------------------------------------------- /lua/vimade/util/key_reducer.lua: -------------------------------------------------------------------------------- 1 | local MATH_FLOOR = math.floor 2 | 3 | -- generates keys at base 62 4 | local KEYS = {0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'} 5 | local TOTAL_KEYS = #KEYS 6 | local TABLE_INSERT = table.insert 7 | local TABLE_SORT = table.sort 8 | 9 | return function() 10 | local id = 1 11 | local cache = {} 12 | local key_reducer = {} 13 | 14 | key_reducer.reduce = function(input) 15 | local key = cache[input] 16 | if key then 17 | return key 18 | end 19 | key = '' 20 | local i = id 21 | local r = 0 22 | while i > 0 do 23 | r = i % TOTAL_KEYS 24 | i = MATH_FLOOR(i / TOTAL_KEYS) 25 | key = KEYS[r + 1] .. key 26 | end 27 | cache[input] = key 28 | id = id + 1 29 | return key 30 | end 31 | key_reducer.reduce_ipairs = function(input) 32 | local output = '' 33 | for _, value in ipairs(input) do 34 | output = output .. key_reducer.reduce(value) .. ',' 35 | end 36 | return output:sub(1,-2) 37 | end 38 | key_reducer.reduce_pairs_key = function(input) 39 | local keys = {} 40 | for key, value in pairs(input) do 41 | TABLE_INSERT(keys, key) 42 | end 43 | TABLE_SORT(keys) 44 | return key_reducer.reduce_ipairs(keys) 45 | end 46 | 47 | return key_reducer 48 | end 49 | -------------------------------------------------------------------------------- /lua/vimade/util/type.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local MATH_CEIL = math.ceil 4 | local DEEP_COMPARE 5 | local DEEP_COPY 6 | local SHALLOW_EXTEND 7 | local RESOLVE_ALL_FN 8 | 9 | local truthyness = function(maybe_num, value) 10 | return maybe_num == value 11 | or ((maybe_num == 1 or maybe_num == true) and (value == 1 or value == true)) 12 | or ((maybe_num == 0 or maybe_num == false) and (value == 0 or value == false)) 13 | end 14 | 15 | M.num_to_bool = function (maybe_num, default) 16 | if maybe_num == nil then 17 | return default 18 | elseif maybe_num == 0 then 19 | return false 20 | else 21 | return true 22 | end 23 | end 24 | 25 | M.str_to_pct = function(pct) 26 | if type(pct) == 'string' then 27 | if pct:sub(-1) == '%' then 28 | return pct:sub(1,-2) / 100 29 | end 30 | end 31 | return nil 32 | end 33 | 34 | M.shallow_compare = function (left, right) 35 | if (left == nil or right == nil) then 36 | return left == right 37 | end 38 | for k, v in pairs(left) do 39 | if right[k] ~= v then 40 | return false 41 | end 42 | end 43 | for k, v in pairs(right) do 44 | if left[k] ~= v then 45 | return false 46 | end 47 | end 48 | return true 49 | end 50 | 51 | M.deep_compare = function (left, right) 52 | if (left == nil or right == nil) then 53 | return left == right 54 | end 55 | local copy = {} 56 | for key, value in pairs(left) do 57 | copy[key] = value 58 | end 59 | for key, value in pairs(right) do 60 | local copy_value = copy[key] 61 | if copy_value == nil then 62 | return false 63 | elseif type(copy_value) ~= type(value) then 64 | return false 65 | elseif type(value) == 'table' then 66 | if DEEP_COMPARE(copy_value, value) == true then 67 | copy[key] = nil 68 | else 69 | return false 70 | end 71 | elseif copy_value == value then 72 | copy[key] = nil 73 | else 74 | return false 75 | end 76 | end 77 | if next(copy) ~= nil then 78 | return false 79 | end 80 | return true 81 | end 82 | DEEP_COMPARE = M.deep_compare 83 | 84 | M.deep_copy = function (value) 85 | if value == nil then 86 | return value 87 | end 88 | local result = {} 89 | for k, v in pairs(value) do 90 | if type(v) == 'table' then 91 | result[k] = DEEP_COPY(v) 92 | else 93 | result[k] = v 94 | end 95 | end 96 | return result 97 | end 98 | DEEP_COPY = M.deep_copy 99 | 100 | M.shallow_copy = function (value) 101 | return SHALLOW_EXTEND({}, value) 102 | end 103 | 104 | M.shallow_extend = function (base, ...) 105 | for i, target in ipairs({...}) do 106 | if target ~= nil then 107 | for key, value in pairs(target) do 108 | base[key] = value 109 | end 110 | end 111 | end 112 | return base 113 | end 114 | SHALLOW_EXTEND = M.shallow_extend 115 | M.extend = SHALLOW_EXTEND 116 | 117 | M.deep_extend = function (base, ...) 118 | base = DEEP_COPY(base) 119 | for i, target in ipairs({...}) do 120 | target = DEEP_COPY(target) 121 | SHALLOW_EXTEND(base, target) 122 | end 123 | return base 124 | end 125 | 126 | M.style_concat = function (...) 127 | local result = {} 128 | for _, target in ipairs({...}) do 129 | for __, value in ipairs(target) do 130 | table.insert(result, value) 131 | end 132 | end 133 | return result 134 | end 135 | 136 | M.resolve_all_fn = function (obj, ...) 137 | if type(obj) == 'function' then 138 | obj = obj(...) 139 | end 140 | if type(obj) == 'table' then 141 | local copy = {} 142 | for i, v in pairs(obj) do 143 | copy[i] = RESOLVE_ALL_FN(v, ...) 144 | end 145 | return copy 146 | end 147 | return obj 148 | end 149 | RESOLVE_ALL_FN = M.resolve_all_fn 150 | 151 | 152 | -- Functions below are intended to be more efficient, they are for internal use only 153 | -- more efficient version of copy, filtering, and equality behaviors for higlights and 154 | -- namespaces 155 | M.copy_hl = function(obj) 156 | local hl = {} 157 | for k, value in pairs(obj) do 158 | hl[k] = value 159 | end 160 | if hl.cterm then 161 | local cterm = {} 162 | hl.cterm = cterm 163 | for k, value in pairs(obj.cterm) do 164 | cterm[k] = value 165 | end 166 | end 167 | return hl 168 | end 169 | 170 | M.copy_hl_ns_gui = function(obj, gui) 171 | local ns = {} 172 | for hl_name, obj_hl in pairs(obj) do 173 | local hl = {} 174 | ns[hl_name] = hl 175 | for k, value in pairs(obj_hl) do 176 | hl[k] = value 177 | end 178 | end 179 | return ns 180 | end 181 | 182 | M.copy_hl_ns_cterm = function(obj, gui) 183 | local ns = {} 184 | for hl_name, obj_hl in pairs(obj) do 185 | local hl = {} 186 | ns[hl_name] = hl 187 | for k, value in pairs(obj_hl) do 188 | hl[k] = value 189 | end 190 | if obj.cterm then 191 | local cterm = {} 192 | hl.cterm = cterm 193 | for k, value in obj.cterm do 194 | cterm[k] = value 195 | end 196 | end 197 | end 198 | return ns 199 | end 200 | 201 | M.filter_ns_gui = function (obj) 202 | for name, hl in pairs(obj) do 203 | if hl.cterm then 204 | hl.cterm = nil 205 | end 206 | if hl.ctermfg then 207 | hl.ctermfg = nil 208 | end 209 | if hl.ctermbg then 210 | hl.ctermbg = nil 211 | end 212 | end 213 | return obj 214 | end 215 | M.filter_ns_cterm = function (obj) 216 | for name, hl in pairs(obj) do 217 | for k, v in pairs(hl) do 218 | if k ~= 'sp' and k ~= 'link' and k:sub(1,5) ~= 'cterm' then 219 | hl[k] = nil 220 | end 221 | end 222 | end 223 | return obj 224 | end 225 | 226 | -- expects pre-filtered ns 227 | M.equal_ns_gui = function (ns_a, ns_b) 228 | for name, hi in pairs(ns_a) do 229 | local target = ns_b[name] 230 | if not target then 231 | return false 232 | end 233 | for k, v in pairs(hi) do 234 | if target[k] ~= v then 235 | return false 236 | end 237 | end 238 | for k, v in pairs(target) do 239 | if not hi[k] then 240 | return false 241 | end 242 | end 243 | end 244 | for name, hi in pairs(ns_b) do 245 | if not ns_a[name] then 246 | return false 247 | end 248 | end 249 | return true 250 | end 251 | 252 | M.equal_ns_cterm = function (ns_a, ns_b) 253 | for name, hi in pairs(ns_a) do 254 | local target = ns_b[name] 255 | if not target 256 | or target.ctermfg ~= hi.ctermfg 257 | or target.ctermbg ~= hi.ctermbg 258 | or target.sp ~= hi.sp then 259 | return false 260 | end 261 | local cterm = hi.cterm 262 | local target_cterm = target.cterm 263 | if (cterm and not target_cterm) 264 | or (not cterm and target_cterm) then 265 | return false 266 | elseif cterm and target_cterm then 267 | for k, v in pairs(cterm) do 268 | if target_cterm[k] ~= v then 269 | return false 270 | end 271 | end 272 | for k, v in pairs(target_cterm) do 273 | if not cterm[k] then 274 | return false 275 | end 276 | end 277 | end 278 | end 279 | return true 280 | end 281 | 282 | return M 283 | -------------------------------------------------------------------------------- /lua/vimade/util/validate.lua: -------------------------------------------------------------------------------- 1 | local COLOR_UTIL = require('vimade.util.color') 2 | local M = {} 3 | 4 | local TO_RGB = COLOR_UTIL.toRgb 5 | local TO_24B = COLOR_UTIL.to24b 6 | local MATH_MIN = math.min 7 | local MATH_MAX = math.max 8 | 9 | local RANGE 10 | local INTENSITY 11 | local RGB 12 | local COLOR 13 | 14 | 15 | M.range = function (value, min, max, default) 16 | default = default or 0 17 | if type(value) ~= 'number' then 18 | value = tonumber(value) 19 | end 20 | if value == nil then 21 | return default 22 | end 23 | return MATH_MIN(MATH_MAX(value, min), max) 24 | end 25 | RANGE = M.range 26 | 27 | M.fade = function (value) 28 | return RANGE(value, 0, 1, 0.4) 29 | end 30 | 31 | M.intensity = function (value) 32 | return RANGE(value, 0, 1, 1) 33 | end 34 | INTENSITY = M.intensity 35 | 36 | M.rgb = function (value) 37 | if type(value) ~= 'table' then 38 | return nil 39 | end 40 | value[1] = RANGE(value[1], 0, 255, 0) 41 | value[2] = RANGE(value[2], 0, 255, 0) 42 | value[3] = RANGE(value[3], 0, 255, 0) 43 | return value 44 | end 45 | RGB = M.rgb 46 | 47 | M.color = function (value, is256) 48 | is256 = is256 or false 49 | if type(value) == 'table' then 50 | return RGB(value) 51 | end 52 | if type(value) == 'string' then 53 | value = TO_24B(value) 54 | end 55 | if is256 then 56 | return RANGE(value, 0, 255, 0) 57 | else 58 | return RANGE(value, 0, 0xFFFFFF, 0) 59 | end 60 | end 61 | COLOR = M.color 62 | 63 | M.invert = function (value) 64 | if type(value) == 'number' then 65 | value = RANGE(value, 0, 1, 0) 66 | return {fg = value, bg = value, sp = value} 67 | end 68 | if type(value) == 'table' then 69 | value.fg = RANGE(value.fg, 0, 1, 0) 70 | value.bg = RANGE(value.bg, 0, 1, 0) 71 | value.sp = RANGE(value.sp, 0, 1, 0) 72 | return value 73 | end 74 | return {fg = 0, bg = 0, sp = 0} 75 | end 76 | 77 | local tint_attr = function (value) 78 | if type(value) ~= 'table' then 79 | return nil 80 | end 81 | if value.rgb then 82 | value.rgb = COLOR(value.rgb) 83 | if type(value.rgb) ~= 'table' and value.rgb then 84 | value.rgb = TO_RGB(value.rgb) 85 | end 86 | value.intensity = INTENSITY(value.intensity) 87 | elseif value.intensity then 88 | value.intensity = INTENSITY(value.intensity) 89 | end 90 | if value.rgb and value.intensity then 91 | return value 92 | end 93 | return nil 94 | end 95 | 96 | M.tint = function (value) 97 | if type(value) ~= 'table' then 98 | return nil 99 | end 100 | for k, v in pairs(value) do 101 | if k ~= 'fg' and k ~= 'bg' and k ~= 'sp' then 102 | value[k] = nil 103 | else 104 | value[k] = tint_attr(v) 105 | end 106 | end 107 | return value 108 | end 109 | 110 | return M 111 | -------------------------------------------------------------------------------- /lua/vimade_legacy_treesitter.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | if vim == nil or vim.api == nil or vim.treesitter == nil or vim.treesitter.highlighter == nil then 4 | return 5 | end 6 | 7 | --local parsers = {} 8 | --local queries = require'nvim-treesitter.query' 9 | --local parsers = require'nvim-treesitter.parsers' 10 | --local hl_map = vim.treesitter.highlighter.hl_map 11 | local lang_map = {} 12 | local seen={} 13 | local hl_is_string = nil 14 | 15 | function dump(t,i) 16 | seen[t]=true 17 | local s={} 18 | local n=0 19 | for k in pairs(t) do 20 | n=n+1 s[n]=k 21 | end 22 | table.sort(s) 23 | for k,v in ipairs(s) do 24 | print(i,v) 25 | v=t[v] 26 | if type(v)=="table" and not seen[v] then 27 | dump(v,k.."\t") 28 | end 29 | end 30 | end 31 | 32 | function M.get_to_eval (bufnr, to_eval) 33 | bufnr = tonumber(bufnr) 34 | if vim.treesitter.highlighter.active[bufnr] == nil then 35 | return 36 | end 37 | if lang_map[bufnr] == nil then 38 | for l in pairs(vim.treesitter.highlighter.active[bufnr]._queries) do 39 | lang_map[bufnr] = l 40 | end 41 | end 42 | local lang = lang_map[bufnr] 43 | if lang == nil then 44 | return nil 45 | end 46 | local query = vim.treesitter.highlighter.active[bufnr]:get_query(lang):query() 47 | local highlighter = vim.treesitter.highlighter.active[bufnr] 48 | 49 | local result = {} 50 | local rows = {} 51 | local startRow = nil 52 | local endRow = -1 53 | for i, eval in pairs(to_eval) do 54 | local row = tonumber(eval[1]) 55 | local col = tonumber(eval[2]) 56 | local end_col = tonumber(eval[3]) 57 | if startRow == nil or row < startRow then 58 | startRow = row 59 | end 60 | if row > endRow then 61 | endRow = row 62 | end 63 | rows[row] = {col,end_col} 64 | end 65 | 66 | highlighter.tree:for_each_tree(function (tstree, tree) 67 | if not tstree then return end 68 | local root_node = tstree:root() 69 | local root_start_row, _, root_end_row, _ = root_node:range() 70 | 71 | local highlighter_query = highlighter:get_query(tree:lang()) 72 | 73 | local q = highlighter_query:query():iter_captures(root_node, bufnr, startRow, endRow + 1) 74 | local matches = q 75 | local next_row = 0 76 | for capture, node in matches do 77 | if capture == nil then 78 | -- pass 79 | else 80 | local capture_name = query.captures[capture] 81 | local hl = highlighter_query.hl_cache[capture] 82 | local rgb= nil 83 | if hl ~= 0 then 84 | if hl_is_string == nil then 85 | hl_is_string = type(hl) == 'string' 86 | end 87 | if hl_is_string == true then 88 | hl = vim.api.nvim_get_hl_id_by_name(hl) 89 | end 90 | if type(hl) == 'number' then 91 | rgb = vim.api.nvim_get_hl_by_id(hl, 1) 92 | end 93 | end 94 | if rgb == nil then 95 | rgb = {} 96 | end 97 | if hl and capture ~= 0 and (rgb['background'] ~= nil or rgb['foreground'] ~= nil) then 98 | local sr,sc,er,ec = node:range() 99 | local r 100 | for r=sr,er do 101 | if rows[r] ~= nil then 102 | local columns = result[r..''] 103 | if columns == nil then 104 | columns = {} 105 | result[r..''] = columns 106 | end 107 | local startCol = rows[r][1] 108 | local endCol = rows[r][2] 109 | local _sc = sc 110 | local _ec = ec 111 | local i 112 | for i=_sc,_ec-1 do 113 | if i >= startCol and i <= endCol then 114 | columns[i..''] = hl 115 | end 116 | end 117 | end 118 | end 119 | end 120 | end 121 | end 122 | end) 123 | return result 124 | end 125 | 126 | function M.get_highlights (bufnr, startRow, endRow, startCol, endCol) 127 | bufnr = tonumber(bufnr) 128 | startRow = tonumber(startRow) 129 | endRow = tonumber(endRow) 130 | startCol = tonumber(startCol) 131 | endCol = tonumber(endCol) 132 | if vim.treesitter.highlighter.active[bufnr] == nil then 133 | return 134 | end 135 | if lang_map[bufnr] == nil then 136 | for l in pairs(vim.treesitter.highlighter.active[bufnr]._queries) do 137 | lang_map[bufnr] = l 138 | end 139 | end 140 | local lang = lang_map[bufnr] 141 | local result = {} 142 | local query = vim.treesitter.highlighter.active[bufnr]:get_query(lang):query() 143 | 144 | local highlighter = vim.treesitter.highlighter.active[bufnr] 145 | highlighter.tree:for_each_tree(function (tstree, tree) 146 | if not tstree then return end 147 | local root_node = tstree:root() 148 | local root_start_row, _, root_end_row, _ = root_node:range() 149 | 150 | if root_start_row > startRow or root_end_row < startRow then return end 151 | 152 | local highlighter_query = highlighter:get_query(tree:lang()) 153 | 154 | local q = highlighter_query:query():iter_captures(root_node, bufnr, startRow, endRow) 155 | local matches = q 156 | local next_row = 0 157 | for capture, node in matches do 158 | if capture == nil then 159 | break 160 | end 161 | local capture_name = query.captures[capture] 162 | --dump(capture) 163 | local hl = highlighter_query.hl_cache[capture] 164 | local rgb= nil 165 | if hl ~= 0 then 166 | if hl_is_string == nil then 167 | hl_is_string = type(hl) == 'string' 168 | end 169 | if hl_is_string == true then 170 | hl = vim.api.nvim_get_hl_id_by_name(hl) 171 | end 172 | rgb = vim.api.nvim_get_hl_by_id(hl, 1) 173 | else 174 | rgb = {} 175 | end 176 | if hl and capture ~= 0 and (rgb['background'] ~= nil or rgb['foreground'] ~= nil) then 177 | local sr,sc,er,ec = node:range() 178 | local r 179 | local _sc = sc 180 | local _ec = endCol 181 | 182 | for r=sr,er do 183 | if r > sr then 184 | _sc = 0 185 | end 186 | if r == er then 187 | _ec = ec - 1 188 | end 189 | if r >= startRow and r < endRow then 190 | local i 191 | for i=_sc,_ec do 192 | if i >= startCol and i < endCol then 193 | result[i..''] = hl 194 | end 195 | end 196 | end 197 | end 198 | end 199 | ::next:: 200 | end 201 | end) 202 | 203 | return result 204 | end 205 | return M 206 | -------------------------------------------------------------------------------- /plugin/vimade.vim: -------------------------------------------------------------------------------- 1 | if exists('g:vimade_loaded') 2 | finish 3 | endif 4 | 5 | let g:vimade_loaded = 1 6 | 7 | if !exists('g:vimade') 8 | let g:vimade = {} 9 | endif 10 | 11 | 12 | ""Enables Vimade 13 | command! VimadeEnable call vimade#Enable() 14 | 15 | ""Unfades all buffers, signs, and disables Vimade 16 | command! VimadeDisable call vimade#Disable() 17 | 18 | ""Disables the current window 19 | command! VimadeWinDisable call vimade#WinDisable() 20 | 21 | ""Disables the current buffer 22 | command! VimadeBufDisable call vimade#BufDisable() 23 | 24 | ""Fades the current buffer 25 | command! VimadeFadeActive call vimade#FadeActive() 26 | " 27 | ""Unfades the current buffer 28 | command! VimadeUnfadeActive call vimade#UnfadeActive() 29 | 30 | ""Enables the current window 31 | command! VimadeWinEnable call vimade#WinEnable() 32 | 33 | ""Enables the current buffer 34 | command! VimadeBufEnable call vimade#BufEnable() 35 | 36 | ""Toggles Vimade between enabled and disabled states 37 | command! VimadeToggle call vimade#Toggle() 38 | 39 | ""Prints debug information that should be included in bug reports 40 | command! VimadeInfo echo json_encode(vimade#GetInfo()) 41 | 42 | ""Recalculates all fades and redraws all inactive buffers and signs 43 | command! VimadeRedraw call vimade#Redraw() 44 | 45 | ""Changes vimade_fadelevel to the {value} specified. {value} can be between 46 | "0.0 and 1.0 47 | command! -nargs=1 VimadeFadeLevel call vimade#FadeLevel() 48 | 49 | ""Changes vimade_fadepriority to the {value} specified. This can be useful 50 | "when combining Vimade with other plugins that also highlight using matches 51 | command! -nargs=1 VimadeFadePriority call vimade#FadePriority() 52 | 53 | ""Overrides the Folded highlight by creating a link to the Vimade base fade. 54 | "This should produce acceptable results for colorschemes that include Folded 55 | "highlights that are distracting in faded windows. 56 | command! VimadeOverrideFolded call vimade#OverrideFolded() 57 | 58 | ""EXPERIMENTAL -- Overrides the SignColumn highlight by creating a link to the Vimade base fade. 59 | "This should produce acceptable results for colorschemes that include Folded 60 | "highlights that are distracting in faded windows. 61 | command! VimadeOverrideSignColumn call vimade#OverrideSignColumn() 62 | 63 | ""EXPERIMENTAL -- Overrides the LineNr highlight by creating a link to the Vimade base fade. 64 | "This should produce acceptable results for colorschemes that include Folded 65 | "highlights that are distracting in faded windows. 66 | command! VimadeOverrideLineNr call vimade#OverrideLineNr() 67 | 68 | ""EXPERIMENTAL -- Overrides the VertSplit highlight by creating a link to the Vimade base fade. 69 | "This should produce acceptable results for colorschemes that include Folded 70 | "highlights that are distracting in faded windows. 71 | command! VimadeOverrideSplits call vimade#OverrideVertSplit() 72 | 73 | ""EXPERIMENTAL -- Overrides the NonText highlight by creating a link to the Vimade base fade. 74 | "This should produce acceptable results for colorschemes that include Folded 75 | "highlights that are distracting in faded windows. 76 | command! VimadeOverrideNonText call vimade#OverrideNonText() 77 | 78 | ""EXPERIMENTAL -- Overrides the EndOfBuffer highlight by creating a link to the Vimade base fade. 79 | "This should produce acceptable results for colorschemes that include Folded 80 | "highlights that are distracting in faded windows. 81 | command! VimadeOverrideEndOfBuffer call vimade#OverrideEndOfBuffer() 82 | 83 | ""EXPERIMENTAL -- Overrides static highlights by creating a link to the Vimade base fade. 84 | "This should produce acceptable results for colorschemes that include Folded 85 | "highlights that are distracting in faded windows. 86 | command! VimadeOverrideAll call vimade#OverrideAll() 87 | 88 | "" EXPERIMENTAL - Loads the current lua-only commands for Neovim (VimadeFocus & VimadeMark) 89 | if has('nvim') 90 | lua require('vimade.focus.commands') 91 | endif 92 | 93 | let g:vimade_plugin_current_directory = resolve(expand(':p:h').'/../lib') 94 | 95 | if (!exists('g:vimade.lazy') || !g:vimade.lazy) && !exists('g::vimade_loaded') 96 | if v:vim_did_enter 97 | call vimade#Load() 98 | else 99 | augroup vimade 100 | au! 101 | au VimEnter * call vimade#Load() 102 | augroup END 103 | endif 104 | endif 105 | -------------------------------------------------------------------------------- /vimade.vimdoc.vim: -------------------------------------------------------------------------------- 1 | "" 2 | "@section Intro 3 | "@order intro contributing support config commands custom-fades normalnc help 4 | "Vimade is an eye catching plugin that fades your inactive buffers. You can think of Vimade as 5 | "2-dimensional infinite syntax list that adjusts itself by reacting to color changes (background, foreground, NormalNC, and colorscheme), 6 | "word wrap, diff groups. Vimade also implements its own 256 color based fading that does its best to preserve colors instead 7 | "of rounding to greys. 8 | 9 | ""@section Contributing 10 | "Open a bug report at 'https://github.com/TaDaa/vimade'. Please include 11 | "'VimadeInfo' in the report and the more 12 | "information the better, but I will gladly look into everything (even just 13 | "general slowness). 14 | 15 | "" 16 | "@section Support 17 | "Vim/NVIM support: 18 | "* Vim 8+ 19 | "* NVIM 20 | "* GUI 21 | "* terminals (256 color and 'set termguicolors') 22 | "* tmux 23 | " 24 | "Terminal support: 25 | "* All terminals are supported when at least 256 colors are active and a background highlight has been specified. 26 | " 27 | "Terminals with background detection: 28 | "- (Vimade can detect the background color inherited from terminal settings) 29 | "* iTerm 30 | "* Tilix 31 | "* Kitty 32 | "* Gnome 33 | "* Rxvt 34 | "* Other terminals that support the ansi codes \033]11;?\007 or \033]11;?\033\\ 35 | 36 | "" 37 | "@section custom-fades 38 | "Vimade allows you to specify custom tints using basebg. You can alter this 39 | "value to any hex code or rgb array (e.g '#ff0000' or [255,0,0]) and the text 40 | "of inactive buffers will fade towards the specified color. You may need to 41 | "adjust the VimadeFadeLevel for favorable results. 42 | " 43 | "For example: 44 | "> 45 | " let vimade.basebg='#ff00000' 46 | " VimadeFadeLevel 0.6 47 | "< 48 | "Will change the tint to red and favor/mix with the original syntax colors 49 | 50 | ""@section normalnc 51 | "If you are using NVIM and enable NormalNC ('hi NormalNC guibg=[color]'), 52 | "Vimade will fade using the NormalNC color, which means you can make a pretty 53 | "sleek looking Vim experience. It might take some effort, but I find the best 54 | "experience to be with background colors that produce lower contrast levels. 55 | 56 | 57 | "" 58 | "@section FAQ/Help, help 59 | "I am using GVIM and my mappings are not working 60 | "- *Add `let g:vimade.usecursorhold=1` to your vimrc* 61 | " 62 | "What about Vim < 8? 63 | "- *Vim 7.4 is currently untested/experimental, but may work if you add `let g:vimade.usecursorhold=1` to your vimrc* 64 | "My colors look off in terminal mode! 65 | "- *Make sure that you either use a supported terminal or colorscheme or manually define the fg and bg for 'Normal'. You can also manually define the tint in your vimade config (g:vimade.basebg and g:vimade.basefg)* 66 | " 67 | "Tmux is not working! 68 | "- *Vimade only works in 256 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 set `set termguicolors` inside vim if your term supports it for an even more accurate level of fading.* 69 | --------------------------------------------------------------------------------