using textprop instead of matches

- updated color parsers to all return RGBA lists
- split alpha-disabled match pruning out into s:UnHlAlpha()
- g:clrzr_maxlines guarding
- single awk job, instead of per-window
- updated awk script to reflect buffer# back to vim
- have plugin check for textprop, termguicolors, ##TextChanged.  echoerr
if missing.
- removed most ...Window() functions & window-specific properties
This commit is contained in:
Jason Stewart 2022-01-02 13:03:39 -05:00
parent e39046cc3d
commit 372a915469
6 changed files with 316 additions and 384 deletions

View file

@ -35,6 +35,9 @@ to blend with.
```
Rebuild helptags with `helptags ALL`, then see `:help clrzr` for more options.
*NOTE:* `clrzr` requires a copy of `awk` that is callable from your system path.
This is used to speed up pattern extraction. Almost any version will do.
### True-Color Support
Works in gVim or any terminal with true-color support. If your terminal is true-color, but

View file

@ -18,7 +18,7 @@ BEGIN {
"(hsl|hsla)\\(" rSpc rFlt cma rPct cma rPct "(" cma rFltOrPct ")?" rSpc "\\)" \
")"
rLineNo = "^" rSpc "[0-9]+\t"
rNumPfx = "^" rSpc "[0-9]+\t"
}
{
@ -29,28 +29,30 @@ BEGIN {
szLine = tolower($1)
# GET LINE#
match(szLine, rLineNo)
if( RLENGTH >= 0 ) {
# BUF#
match(szLine, rNumPfx)
szBufNo = substr(szLine, RSTART, RLENGTH - 1)
szLine = substr(szLine, RSTART + RLENGTH)
szLineNo = substr(szLine, RSTART, RLENGTH - 1)
szLine = substr(szLine, RSTART + RLENGTH)
colAbs = 0
# LINE#
match(szLine, rNumPfx)
szLineNo = substr(szLine, RSTART, RLENGTH - 1)
szLine = substr(szLine, RSTART + RLENGTH)
# GET COLORS WITHIN LINE
while( 1 ) {
# GET COLORS WITHIN LINE
colAbs = 0
while( 1 ) {
match(szLine, rExpr)
colAbs += RSTART
match(szLine, rExpr)
colAbs += RSTART
if( RLENGTH < 0 ) {
break
}
printf "%s|%d|%d|%s\n", szLineNo, colAbs, RLENGTH, substr(szLine, RSTART, RLENGTH)
colAbs += RLENGTH - 1
szLine = substr(szLine, RSTART + RLENGTH)
if( RLENGTH < 0 ) {
break
}
printf "%s|%s|%d|%d|%s\n", szBufNo, szLineNo, colAbs, RLENGTH, substr(szLine, RSTART, RLENGTH)
colAbs += RLENGTH - 1
szLine = substr(szLine, RSTART + RLENGTH)
}
}

View file

@ -12,6 +12,15 @@ let s:keepcpo = &cpo
set cpo&vim
" TODO: TESTS
" - CTRL-W n for new split
" - open multiple, turn off, turn on
" - awk vs unicode. (line2byte, byte2line?)
" TODO: allowed filetypes list?
" TODO: sign_column (bigger pls, :h sign-commands)?
" TODO: option to refresh current line on SafeState?
" --------------------------- CONSTANTS ---------------------------
@ -47,14 +56,20 @@ function! s:WriteDebugBuf(object)
endfunction
" WRITE AUTOCMD EVENTS TO QUICKFIX LIST (for event inspection)
" WRITE AUTOCMD EVENTS TO DEBUG BUFFER
function! s:SnoopEvent(evt)
echomsg [bufnr('%'), win_getid(), a:evt, v:event]
let n_buf = bufnr()
if n_buf == 1
call s:WriteDebugBuf([a:evt, 'BUF', n_buf, 'LN', line('.'), 'EVT', v:event])
endif
endfunction
function! s:Debug(var)
if 0 | echomsg a:var | endif
if 0
let l_msg = a:var + ['BUF=' . bufnr(), 'ENBL=' . s:IsEnabled()]
echomsg join(l_msg, '|')
endif
endfunction
@ -89,24 +104,10 @@ function! s:IntAlphaMix(rgba_fg, rgb_bg)
endfunction
" CHOOSES A REASONABLE FOREGROUND COLOR FOR A GIVEN BACKGROUND COLOR
" takes an [R,G,B] int color list
" returns an 'RRGGBB' hex color string
function! s:FGforBGList(bg) "{{{1
" TODO: needs some work
let fgc = g:clrzr_fgcontrast
if (a:bg[0]*30 + a:bg[1]*59 + a:bg[2]*11) > 12000
return s:predefined_fgcolors['dark'][fgc][1:]
else
return s:predefined_fgcolors['light'][fgc][1:]
end
endfunction
" --------------------------- COLOR/PATTERN EXTRACTORS ---------------------------
function! s:IsAlphaFirst()
let is_alpha_first = get(w:, 'clrzr_hex_alpha_first', get(g:, 'clrzr_hex_alpha_first', 0))
let is_alpha_first = get(b:, 'clrzr_hex_alpha_first', get(g:, 'clrzr_hex_alpha_first', 0))
return (is_alpha_first == 1)
endfunction
@ -115,7 +116,8 @@ endfunction
" RGB: #00f #0000ff
" RGBA: #00f8 #0000ff88
" or ARGB: #800f #880000ff
function! s:HexCode(color_text_in, rgb_bg) "{{{2
" returns [r,g,b,a]
function! s:HexCode(color_text_in) "{{{2
let rx_color_prefix = '%(#|0x)'
let is_alpha_first = s:IsAlphaFirst()
@ -126,7 +128,6 @@ function! s:HexCode(color_text_in, rgb_bg) "{{{2
" SPLIT INTO COMPONENT VALUES
let lColor = [0xff,0xff,0xff,0xff]
let matchcolor = foundcolor . '\ze'
if colorlen == 8
for ix in [0,1,2,3]
@ -134,15 +135,6 @@ function! s:HexCode(color_text_in, rgb_bg) "{{{2
let lColor[ix] = str2nr(foundcolor[ic:(ic+1)], 16)
endfor
" END MATCH AT COLOR DIGITS WHEN ALPHA DISPLAY IS UNAVAILABLE
if empty(a:rgb_bg)
if is_alpha_first
let matchcolor = '\x\x\zs' . foundcolor[2:7] . '\ze'
else
let matchcolor = foundcolor[0:5] . '\ze\x\x'
endif
endif
elseif colorlen == 6
for ix in [0,1,2]
@ -157,15 +149,6 @@ function! s:HexCode(color_text_in, rgb_bg) "{{{2
let lColor[ix] = or(lColor[ix], lColor[ix] * 16)
endfor
" END MATCH AT COLOR DIGITS WHEN ALPHA DISPLAY IS UNAVAILABLE
if empty(a:rgb_bg)
if is_alpha_first
let matchcolor = '\x\zs' . foundcolor[1:3] . '\ze'
else
let matchcolor = foundcolor[0:2] . '\ze\x'
endif
endif
elseif colorlen == 3
for ix in [0,1,2]
@ -180,19 +163,13 @@ function! s:HexCode(color_text_in, rgb_bg) "{{{2
let lColor = lColor[1:3] + [lColor[0]]
endif
" MIX WITH BACKGROUND COLOR, IF SET
if !empty(a:rgb_bg)
let lColor = s:IntAlphaMix(lColor, a:rgb_bg)
endif
" RETURN [SYNTAX PATTERN, RGB COLOR LIST]
let sz_pat = join([rx_color_prefix, matchcolor, '>'], '')
return [sz_pat, lColor[0:2]]
return lColor
endfunction
" DECONSTRUCTS rgb(255,128,64)
" returns [r,g,b,a] with alpha-value = 0xFF
function! s:RgbColor(color_text_in) "{{{2
" REGEX: COLOR EXTRACT
@ -200,35 +177,34 @@ function! s:RgbColor(color_text_in) "{{{2
" EXTRACT COLOR COMPONENTS
let rgb_matches = matchlist(a:color_text_in, '\v\(\s*' . rx_colors)
if empty(rgb_matches) | return ['',[]] | endif
if empty(rgb_matches) | return [] | endif
" NORMALIZE TO NUMBER
let lColor = []
for ix in [1,3,5]
let c_cmpnt = str2float(rgb_matches[ix])
if rgb_matches[ix+1] == '%'
let rgb_matches[ix+1] = '\%' " ESCAPE FOR FOLLOWING MATCH REGEX
let c_cmpnt = (c_cmpnt * 255.0) / 100.0
endif
if (c_cmpnt < 0.0) || (c_cmpnt > 255.0) | return ['',[]] | endif
" SKIP INVALID COLORS
if (c_cmpnt < 0.0) || (c_cmpnt > 255.0) | return [] | endif
call add(lColor, float2nr(round(c_cmpnt)))
endfor
" SKIP INVALID COLORS
if len(lColor) < 3 | continue | endif
" BUILD HIGHLIGHT PATTERN
let sz_pat = call(
\ 'printf',
\ [ '<rgb\(\s*%s%s\s*,\s*%s%s\s*,\s*%s%s\s*\)'] + rgb_matches[1:6]
\ )
return [sz_pat, lColor]
" ADD ALPHA PLACE HOLDER & RETURN
call add(lColor, 0xFF)
return lColor
endfunction
" DECONSTRUCTS: hsl(195, 100%, 50%)
" returns [r,g,b,a] with alpha-value = 0xFF
function! s:HslColor(color_text_in) "{{{2
" REGEX: COLOR EXTRACT
@ -242,7 +218,7 @@ function! s:HslColor(color_text_in) "{{{2
" EXTRACT COLOR COMPONENTS
let hsl_matches = matchlist(a:color_text_in, rx_colors)
if empty(hsl_matches) | return ['',[]] | endif
if empty(hsl_matches) | return [] | endif
" HUE TO NUMBER
let hue = fmod(str2float(hsl_matches[1]), 360.0)
@ -252,13 +228,10 @@ function! s:HslColor(color_text_in) "{{{2
" SATURATION & LIGHTNESS TO NUMBER
for ix in [2,3]
let c_cmpnt = str2float(hsl_matches[ix]) / 100.0
if (c_cmpnt < 0.0) || (c_cmpnt > 1.0) | return ['',[]] | endif
if (c_cmpnt < 0.0) || (c_cmpnt > 1.0) | return [] | endif
call add(lColor, c_cmpnt)
endfor
" SKIP INVALID COLORS
if len(lColor) < 3 | continue | endif
" HSL -> RGB
let chroma = (1.0 - abs((2.0*lColor[2]) - 1.0)) * lColor[1]
let hprime = hue / 60.0
@ -281,37 +254,24 @@ function! s:HslColor(color_text_in) "{{{2
let lColor = map(lColor, {_, v -> float2nr(round((v+mval) * 255.0))})
" BUILD HIGHLIGHT PATTERN
let sz_pat = call(
\ 'printf',
\ [ '<hsl\(\s*%s\s*,\s*%s\%%\s*,\s*%s\%%\s*\)'] + hsl_matches[1:3]
\ )
return [sz_pat, lColor]
" ADD ALPHA PLACE HOLDER & RETURN
call add(lColor, 0xFF)
return lColor
endfunction
" HANDLES ALPHA VERSIONS OF RGB/HSL
function! s:AlphaColor(Color_Func, pat_search, pat_replace, color_text_in, rgb_bg) "{{{2
" returns [r,g,b,a] with alpha-value from Color_Func() call populated
function! s:AlphaColor(Color_Func, color_text_in)
" GET BASE COLOR
let [pat_hsl, lColor] = a:Color_Func(a:color_text_in)
if empty(pat_hsl) || empty(lColor)
return ['', []]
endif
let pat_hsl = substitute(pat_hsl, a:pat_search, a:pat_replace, '')
" SKIP MIXING WHEN BGCOLOR UNSPECIFIED
if empty(a:rgb_bg)
let pat_hsl = substitute(pat_hsl, '\\)', ',', '')
return [pat_hsl, lColor]
endif
let lColor = a:Color_Func(a:color_text_in)
if empty(lColor) | return lColor | endif
" EXTRACT ALPHA COMPONENT
let alpha_match = matchlist(a:color_text_in, '\v(' . s:RXPCTORFLT . ')\s*\)')
if empty(alpha_match) | return ['', []] | endif
if empty(alpha_match) | return lColor | endif
let alpha_suff = alpha_match[1]
" PARSE ALPHA TO [0.0, 1.0]
@ -319,37 +279,23 @@ function! s:AlphaColor(Color_Func, pat_search, pat_replace, color_text_in, rgb_b
let alpha = 2.0
if ix_pct > -1
let alpha = str2float(alpha_suff[:ix_pct-1]) / 100.0
" escape trailing percent sign
let alpha_suff = escape(alpha_suff, '%')
else
let alpha = str2float(alpha_suff)
endif
" SKIP COLORS WITH INVALID ALPHA
if alpha > 1.0 | return [] | endif
" SCALE TO [0, 255]
if alpha <= 1.0
call add(lColor, float2nr(round(alpha * 255.0)))
else
return ['',[]]
endif
" MIX COLOR WITH BACKGROUND
let lColor = s:IntAlphaMix(lColor, a:rgb_bg)
" UPDATE HIGHLIGHT PATTERN
" escape decimal point, then escape all slashes in the suffix
let hsl_suff = escape(escape(alpha_suff, '.'), '\')
" replace alpha suffix into pattern
let pat_hsl = substitute(pat_hsl, '\\)', ',\\s*' . hsl_suff . '\\s*\\)','')
return [pat_hsl, lColor]
let lColor[3] = float2nr(round(alpha * 255.0))
return lColor
endfunction
" PLUGIN IS EFFECTIVELY OFF FOR THE CURRENT WINDOW
" WHEN `w:clrzr_matches` IS NOT A DICT
function! s:IsEnabledInWindow()
return exists('w:clrzr_matches') && (type(w:clrzr_matches) == v:t_dict)
function! s:IsEnabled()
return exists('s:clrzr_on') && (s:clrzr_on == 1)
endfunction
@ -357,7 +303,12 @@ endfunction
" FROM l_first TO l_last
function! s:PreviewColorInLine(l_first, l_last) "{{{1
if !s:IsEnabledInWindow() | return | endif
call s:Debug(['PCIL', a:l_first, a:l_last])
if !s:IsEnabled() | return | endif
" SKIP PROCESSING NON_EXISTENT BUFFERS
let n_buf = bufnr()
if n_buf < 0 | return | endif
" SKIP PROCESSING HELPFILES (usually large)
if getbufvar('%', '&syntax') ==? 'help' | return | endif
@ -365,12 +316,14 @@ function! s:PreviewColorInLine(l_first, l_last) "{{{1
let l_range = sort([
\ (type(a:l_first) == v:t_string) ? line(a:l_first) : a:l_first,
\ (type(a:l_last) == v:t_string) ? line(a:l_last) : a:l_last,
\ ])
\ ])
" ONLY PARSE UP TO g:clrzr_maxlines
if g:clrzr_maxlines > 0
if l_range[0] > g:clrzr_maxlines | return | endif
if l_range[1] > g:clrzr_maxlines | let l_range[1] = g:clrzr_maxlines | endif
let n_maxlines = get(g:, 'clrzr_maxlines', -1)
if type(n_maxlines) != v:t_number | let n_maxlines = -1 | endif
if n_maxlines > 0
if l_range[0] > n_maxlines | return | endif
if l_range[1] > n_maxlines | let l_range[1] = n_maxlines | endif
endif
" GET LINES FROM CURRENT BUFFER
@ -380,179 +333,198 @@ function! s:PreviewColorInLine(l_first, l_last) "{{{1
" CACHE CURRENT BACKGROUND COLOR FOR ALPHA BLENDING
let s:rgb_bg = s:RgbBgColor()
" CLEAR PROPS FOR UPDATE RANGE
call prop_clear(l_range[0], l_range[1])
" WRITE LINES TO AWK CHANNEL
let line_no = l_range[0]
if exists('w:clrzr_awk_chan')
let sts = ch_status(w:clrzr_awk_chan)
if exists('s:awk_chan')
let sts = ch_status(s:awk_chan)
if sts == 'open'
call s:Debug(['WRITE LINES', len(lines), 'WIN', win_getid(), w:clrzr_awk_chan])
call s:Debug(['PCIL: WRITE', len(lines)])
for line in lines
call ch_sendraw(w:clrzr_awk_chan, line_no . "\t" . line . "\n")
call ch_sendraw(s:awk_chan, n_buf . "\t" . line_no . "\t" . line . "\n")
let line_no += 1
endfor
call ch_sendraw(w:clrzr_awk_chan, "--END--\n")
call ch_sendraw(s:awk_chan, "--END--\n")
else
echomsg "AWK CHANNEL STATUS = " . sts
echoerr "AWK CHANNEL STATUS = " . sts
endif
else
echomsg "AWK CHANNEL NOT SET!"
echoerr "AWK CHANNEL NOT SET!"
endif
endfunction
function! s:UnHlAlpha(text, n_col, n_length)
let l_col = a:n_col
let l_len = a:n_length
if a:text[0] == '#' || a:text[0] == '0'
let ix_hex = match(a:text, '\v\x+')
if ix_hex > -1
let af = s:IsAlphaFirst()
let sz_hex = a:text[ix_hex:]
if len(sz_hex) == 8
let l_len -= af ? ix_hex + 2 : 2
let l_col += af ? ix_hex + 2 : 0
elseif len(sz_hex) == 4
let l_len -= af ? ix_hex + 1 : 1
let l_col += af ? ix_hex + 1 : 0
endif
endif
elseif (a:text[0:3] ==# 'rgba') || (a:text[0:3] ==# 'hsla')
let ix_tail = match(a:text, '\v,[^,]*\)')
if ix_tail > -1 | let l_len = ix_tail | endif
endif
return [l_col, l_len]
endfunction
" EXTRACTS COLOR DATA FROM MATCHES & CREATES HIGHLIGHT GROUPS
function! s:ProcessMatch(match)
if !s:IsEnabledInWindow() | return | endif
if !exists('w:clrzr_matches_next')
let w:clrzr_matches_next = {}
endif
if !s:IsEnabled() | return | endif
if a:match == '--END--'
" DELETE REMAINING ITEMS IN OLD MAP
call s:ClearWindow()
" UPDATE TO NEW MAP
let w:clrzr_matches = w:clrzr_matches_next
unlet w:clrzr_matches_next
" NOTE: can do end-of-batch work here if needed
return
endif
" EXTRACT SOURCE INFORMATION FROM AWK OUTPUT
let lParts = split(a:match, '|')
if len(lParts) < 4 | return | endif
if len(lParts) < 5 | return | endif
let [n_buf, n_line, n_col, n_length] = map(lParts[0:3], {_,v -> str2nr(trim(v))})
if n_buf < 0
echoerr 'INVALID BUF'
return
endif
" EXTRACT COLOR INFORMATION FROM SYNTAX
" returns [r,g,b,a] lists
" RETURNS [syntax_pattern, rgb_color_list]
let color_text = lParts[3]
let pat = ''
let rgb_color = []
let color_text = lParts[4]
let rgba_color = []
if color_text[0] == '#' || color_text[0] == '0'
let [pat, rgb_color] = s:HexCode(color_text, s:rgb_bg)
elseif color_text[0:3] ==? 'rgba'
let [pat, rgb_color] = s:AlphaColor(function('s:RgbColor'), 'rgb', 'rgba', color_text, s:rgb_bg)
elseif color_text[0:3] ==? 'hsla'
let [pat, rgb_color] = s:AlphaColor(function('s:HslColor'), 'hsl', 'hsla', color_text, s:rgb_bg)
elseif color_text[0:2] ==? 'rgb'
let [pat, rgb_color] = s:RgbColor(color_text)
elseif color_text[0:2] ==? 'hsl'
let [pat, rgb_color] = s:HslColor(color_text)
let rgba_color = s:HexCode(color_text)
elseif color_text[0:3] ==# 'rgba'
let rgba_color = s:AlphaColor(function('s:RgbColor'), color_text)
elseif color_text[0:3] ==# 'hsla'
let rgba_color = s:AlphaColor(function('s:HslColor'), color_text)
elseif color_text[0:2] ==# 'rgb'
let rgba_color = s:RgbColor(color_text)
elseif color_text[0:2] ==# 'hsl'
let rgba_color = s:HslColor(color_text)
else
return
endif
if empty(pat) || empty(rgb_color)
return
" SKIP INVALID COLORS
if empty(rgba_color) | return | endif
let rgb_color = rgba_color[0:2]
if rgba_color[3] < 0xFF
if empty(s:rgb_bg)
" TRIM ALPHA COMPONENT IF BG COLOR IS UNKNOWN
let [n_col, n_length] = s:UnHlAlpha(color_text, n_col, n_length)
else
" ALPHA-BLEND TRANSPARENCIES
let rgb_color = s:IntAlphaMix(rgba_color, s:rgb_bg)
endif
endif
" NOTE: pattern & color must be tracked independently, since
" multiple alpha patterns may map to the same highlight color
let hex_color = call('printf', ['%02x%02x%02x'] + rgb_color)
" NOTE: text properties are per-buffer `text-prop-intro`
" NOTE [line, col, width]
let lPos = map(lParts[0:2], {_,v -> str2nr(trim(v))})
let cache_key = join(lPos + [hex_color], '_')
" CHECK IF TEXTPROP GROUP EXISTS
" NOTE: key by full RGBA, so as not to create new groups
" on :colorscheme change
let group = 'Czr' . call('printf', ['%02x%02x%02x%02x'] + rgba_color)
let pt = prop_type_get(group, {'bufnr': n_buf})
let b_create = empty(pt)
" HIGHLIGHT EXISTS: copy into new map, skip highlight commands, delete from old map
if has_key(w:clrzr_matches, cache_key)
let w:clrzr_matches_next[cache_key] = w:clrzr_matches[cache_key]
call remove(w:clrzr_matches, cache_key)
" NEW HIGHLIGHT: add, copy into new map
elseif !has_key(w:clrzr_matches_next, cache_key)
" INSERT HIGHLIGHT
" NOTE: always do this (even when :hl group exists) to accommodate
" :colorscheme changes, since highlight groups are never
" truly deleted -- there goes my hlexists() optimization
let group = 'Clrzr' . hex_color
let guifg = g:clrzr_fgcontrast < 0 ? 'fg' : ('#' . s:FGforBGList(rgb_color))
let cmd_highlight = [
\ 'highlight',
\ group,
\ 'guibg=#' . hex_color,
\ 'guifg=' . guifg,
\]
" \ 'guifg=#' .
execute join(cmd_highlight, ' ')
" INSERT MATCH PATTERN FOR HIGHLIGHT
let w:clrzr_matches_next[cache_key] = matchaddpos(group, [lPos])
let cmd_highlight = [
\ 'highlight',
\ group,
\ 'guibg=#' . call('printf', ['%02x%02x%02x'] + rgb_color),
\ 'guifg=fg',
\]
execute join(cmd_highlight, ' ')
if b_create
let pt = prop_type_add(group, {
\ 'bufnr': n_buf,
\ 'highlight': group,
\ 'combine': 0,
\ 'start_incl': 0,
\ 'end_incl': 0,
\})
endif
" INSERT MATCH PATTERN FOR HIGHLIGHT
call prop_add(n_line, n_col, {
\ 'bufnr': n_buf,
\ 'length': n_length,
\ 'type': group,
\ })
endfunction
" --------------------------- WINDOW FUNCTIONS ---------------------------
" --------------------------- EXPOSED COMMANDS ---------------------------
function s:ForeachWindow(Func)
let lInf = getwininfo()
for inf in lInf
call win_execute(inf.winid, 'call a:Func()')
endfor
function! clrzr#Refresh()
let n_end = line('$')
call s:Debug(['REFRESH', n_end])
call s:PreviewColorInLine(1, n_end)
endfunction
" CLEAR HIGHLIGHTS IN CURRENT WINDOW
function! s:ClearWindow()
if !s:IsEnabledInWindow() | return | endif
" DELETE MATCHES
for match_key in keys(w:clrzr_matches)
try
call matchdelete(w:clrzr_matches[match_key])
catch /.*/
" matches have been cleared in other ways, e.g. user has called clearmatches()
endtry
endfor
let w:clrzr_matches = {}
" TOGGLES ALPHA COMPONENT POSITION FOR HEX COLORS IN CURRENT WINDOW
function! clrzr#AlphaPosToggle()
if s:IsAlphaFirst()
let b:clrzr_hex_alpha_first = 0
else
let b:clrzr_hex_alpha_first = 1
endif
call clrzr#Refresh()
endfunction
function! s:AwkOut(chan, msg)
" SEND a:msg TO COLOR MATCHER FOR THE CORRECT WINDOW
if exists('g:clrzr_chanid2winid')
let chanid = string(a:chan)
if has_key(g:clrzr_chanid2winid, chanid)
call win_execute(g:clrzr_chanid2winid[chanid], 'call s:ProcessMatch(a:msg)')
endif
endif
call s:ProcessMatch(a:msg)
endfunction
function! s:AwkErr(chan, msg)
" TODO: disable window w/ error
echomsg ['ERR', a:msg, 'WIN', win_getid()]
echoerr ['ERR', a:msg]
endfunction
function! s:AwkExit(job_id, exit)
" TODO: disable window w/ error
call s:Debug(['EXIT', a:job_id, a:exit, 'WIN', win_getid()])
call s:Debug(['EXIT', a:job_id, a:exit])
endfunction
const s:CLRZR_AWK_SCRIPT_PATH = expand('<sfile>:p:h') . '/clrzr.awk'
let g:clrzr_chanid2winid = {}
function! s:EnableWindow(b_reparse)
if s:IsEnabledInWindow() | return | endif
function! clrzr#Enable()
call s:Debug(['ENABLE', win_getid()])
if s:IsEnabled() | return | endif
call s:Debug(['ENABLE'])
let job_opts = {
\ 'in_mode': 'nl',
@ -570,81 +542,30 @@ function! s:EnableWindow(b_reparse)
let job_opts['noblock'] = 1
endif
let w:clrzr_awk_job = job_start(
let s:awk_job = job_start(
\ ['awk', '-f', s:CLRZR_AWK_SCRIPT_PATH],
\ job_opts)
" NOTE: string(chan) == 'channel fail' is error
let w:clrzr_awk_chan = job_getchannel(w:clrzr_awk_job)
if string(w:clrzr_awk_chan) == 'channel fail'
echomsg 'JOB CHANNEL FAILURE'
unlet w:clrzr_awk_chan
let s:awk_chan = job_getchannel(s:awk_job)
if string(s:awk_chan) == 'channel fail'
echoerr 'JOB CHANNEL FAILURE'
unlet s:awk_chan
unlet s:awk_job
return
else
let g:clrzr_chanid2winid[string(w:clrzr_awk_chan)] = win_getid()
let w:clrzr_matches = {}
if a:b_reparse | call s:ReparseWindow() | endif
endif
let s:clrzr_on = 1
endfunction
function! s:DisableWindow()
call s:ClearWindow()
" END JOB, REMOVE chan2winid MAPPING
if exists('w:clrzr_awk_job')
call remove(g:clrzr_chanid2winid, string(w:clrzr_awk_chan))
call job_stop(w:clrzr_awk_job)
endif
" REMOVE clrzr_ WINDOW STATE
for wk in keys(w:)
if match(wk, '^clrzr_') > -1
call remove(w:, wk)
" REFRESH ALL BUFFERS ON RE-ENABLE
" LEAVE THIS TO AUTOCMDS FOR FIRST RUN
" I believe autocmds & manuals were troubling each other...
if exists('s:first_ran')
call clrzr#RefreshAllBuffers()
else
let s:first_ran = 1
endif
endfor
endfunction
function! s:ReparseWindow()
call s:PreviewColorInLine(1, '$')
endfunction
" --------------------------- EXPOSED COMMANDS ---------------------------
function! clrzr#Refresh()
call s:DisableWindow()
call s:EnableWindow(1)
endfunction
" TOGGLES COLOR HIGHLIGHTING IN CURRENT WINDOW
function! clrzr#ToggleWindow()
if s:IsEnabledInWindow()
call s:DisableWindow()
else
call s:EnableWindow(1)
endif
endfunction
" TOGGLES ALPHA COMPONENT POSITION FOR HEX COLORS IN CURRENT WINDOW
function! clrzr#AlphaPosToggleWindow()
if s:IsAlphaFirst()
let w:clrzr_hex_alpha_first = 0
else
let w:clrzr_hex_alpha_first = 1
endif
call s:ClearWindow()
call s:ReparseWindow()
endfunction
function! clrzr#Enable()
augroup Clrzr
@ -654,16 +575,20 @@ function! clrzr#Enable()
if 0
const probe = [
\ 'WinNew',
\ 'WinEnter',
\ 'BufReadPost',
\ 'FileReadPost',
\ 'StdinReadPost',
\ 'FileChangedShellPost',
\ 'ShellFilterPost',
\ 'ColorScheme',
\ 'TextChanged',
\ 'SafeState',
\ 'InsertEnter',
\ 'CursorHoldI',
\ 'InsertLeave',
\ ]
" \ 'WinNew',
" \ 'WinEnter',
" \ 'BufReadPost',
" \ 'FileReadPost',
" \ 'StdinReadPost',
" \ 'FileChangedShellPost',
" \ 'ShellFilterPost',
" \ 'ColorScheme',
for evt in probe
let cmd = ['autocmd', evt, '* call s:SnoopEvent("' . evt . '")']
@ -672,99 +597,100 @@ function! clrzr#Enable()
else
" HIGHLIGHTS ARE PER-WINDOW, SO RE-BUILD HIGHLIGHTS,
" EVEN WITH FILES PREVIOUSLY LOADED IN DIFFERENT WINDOWS
" 0: Don't reparse syntax. Will be followed by WinEnter
" which handles this.
autocmd WinNew * call s:EnableWindow(0)
" RESCAN ON WINDOW SWITCH (case: edit to one buffer in mulitple splits)
autocmd WinEnter * call s:ReparseWindow()
" REBUILD HIGHLIGHTS AFTER READS
autocmd BufReadPost,FileReadPost,StdinReadPost,FileChangedShellPost * call s:ReparseWindow()
autocmd BufReadPost,FileReadPost,StdinReadPost,FileChangedShellPost * call clrzr#Refresh()
" NOTE: FilterReadPost isn't triggered when `shelltemp` is off,
" but ShellFilterPost is
autocmd ShellFilterPost * call s:ReparseWindow()
" but ShellFilterPost is regardless
autocmd ShellFilterPost * call clrzr#Refresh()
" FORCE-REBUILD HIGHLIGHTS AFTER COLORSCHEME CHANGE
" (to re-blend alpha colors with new background color)
" NOTE: refreshes all windows in case bg color changed
autocmd ColorScheme * call s:ForeachWindow(function('clrzr#ResetWindow'))
" TODO: allowed filetypes list?
" TODO: sign_column (bigger pls, :h sign-commands)?
"
" TODO: investigate potential autocmd loop
" TODO: what to do on insert mode edits (moving highlights down
" correctly)
" getmatches, clearmatches, setmatches
" TODO: textprop error on bufdo from autocmd
" change colors, or enable/disable,
" with two views into same buffer to reproduce
" E966
autocmd ColorScheme * call clrzr#RefreshAllBuffers()
" :let timer = timer_start(500,
" \ {-> execute("echo 'Handler called'", "")},
" \ {'repeat': 3})
" REFRESH ON NORMAL MODE CHANGES
autocmd TextChanged * call s:ReparseWindow()
autocmd TextChanged * call clrzr#Refresh()
" INSERT MODE: REFRESH WHEN DIRTY, CURSOR PAUSE & EXIT
autocmd InsertEnter * let w:clrzr_changedtick = b:changedtick
autocmd InsertEnter * let b:clrzr_changedtick = b:changedtick
autocmd CursorHoldI * call s:RefreshIfDirty()
autocmd InsertLeave * call s:RefreshIfDirty()
augroup END
call s:ForeachWindow(function('s:EnableWindow', [1]))
endif
endfunction
function! s:RefreshIfDirty()
if exists('w:clrzr_changedtick')
if w:clrzr_changedtick != b:changedtick
call s:ReparseWindow()
if exists('b:clrzr_changedtick')
if b:clrzr_changedtick != b:changedtick
call clrzr#Refresh()
endif
unlet w:clrzr_changedtick
unlet b:clrzr_changedtick
endif
endfunction
function! s:RemoveClrzrKeys(dict)
for wk in keys(a:dict)
if match(wk, '^clrzr_') > -1
call remove(a:dict, wk)
endif
endfor
endfunction
function! s:ResetBuffer()
call prop_clear(1, line('$'))
call s:RemoveClrzrKeys(b:)
endfunction
" REMOVE AUTOGROUP & CLEAR HIGHLIGHTS ACROSS ALL WINDOWS
function! clrzr#Disable()
augroup Clrzr
au!
augroup END
augroup! Clrzr
call s:ForeachWindow(function('s:DisableWindow'))
if exists('s:clrzr_on') | unlet s:clrzr_on | endif
" END JOB, REMOVE chan2winid MAPPING
if exists('s:awk_job')
call job_stop(s:awk_job)
unlet s:awk_chan
unlet s:awk_job
endif
" REMOVE clrzr_ STATE
let n_buf = bufnr()
bufdo call s:ResetBuffer()
if n_buf > -1 | execute n_buf . 'buffer' | endif
endfunction
function! clrzr#ResetWindow()
call s:ClearWindow()
call s:ReparseWindow()
function! clrzr#RefreshAllBuffers()
let n_buf = bufnr()
bufdo call clrzr#Refresh()
if n_buf > -1 | execute n_buf . 'buffer' | endif
endfunction
" --------------------------- SETUP ---------------------------
let s:predefined_fgcolors = {
\ 'dark': ['#444444', '#222222', '#000000'],
\ 'light': ['#bbbbbb', '#dddddd', '#ffffff'],
\}
if !exists("g:clrzr_fgcontrast")
" Default to black / white
let g:clrzr_fgcontrast = len(s:predefined_fgcolors['dark']) - 1
elseif g:clrzr_fgcontrast >= len(s:predefined_fgcolors['dark'])
echohl WarningMsg
echo "g:clrzr_fgcontrast value invalid, using default"
echohl None
let g:clrzr_fgcontrast = len(s:predefined_fgcolors['dark']) - 1
endif
let s:saved_fgcontrast = g:clrzr_fgcontrast
" Restoration and modelines
let &cpo = s:keepcpo

View file

@ -18,17 +18,8 @@ This plugin defines three commands:
*:ClrzrOn* - enable color highlighting in all windows
*:ClrzrOff* - disable color highlighting in all windows
*:ClrzrAposTog* - toggle alpha position for hex colors in current window
*:ClrzrRefresh* - refresh highlights in current window
*:ClrzrTog* - toggle highlights in current window
*'g:clrzr_fgcontrast'*
To use solid color highlight, set this in your vimrc (later change won't
probably take effect unless you use ':ClrzrRefresh' to force update):
let g:clrzr_fgcontrast = -1
Set it to 0 or 1 to use a softened foregroud color.
*:ClrzrAposTog* - toggle alpha position for hex colors in current buffer
*:ClrzrRefresh* - refresh highlights in current buffer
*'g:clrzr_startup'*
If you don't want to enable clrzr at startup, set the following:

View file

@ -1,5 +1,4 @@
'clrzr' clrzr.txt /*'clrzr'*
'g:clrzr_fgcontrast' clrzr.txt /*'g:clrzr_fgcontrast'*
'g:clrzr_hex_alpha_first' clrzr.txt /*'g:clrzr_hex_alpha_first'*
'g:clrzr_maxlines' clrzr.txt /*'g:clrzr_maxlines'*
'g:clrzr_startup' clrzr.txt /*'g:clrzr_startup'*
@ -7,6 +6,5 @@
:ClrzrOff clrzr.txt /*:ClrzrOff*
:ClrzrOn clrzr.txt /*:ClrzrOn*
:ClrzrRefresh clrzr.txt /*:ClrzrRefresh*
:ClrzrTog clrzr.txt /*:ClrzrTog*
Clrzr clrzr.txt /*Clrzr*
clrzr.txt clrzr.txt /*clrzr.txt*

View file

@ -10,25 +10,37 @@
" Usage:
"
" Reload guard and 'compatible' handling {{{1
if exists("loaded_clrzr") || v:version < 700 || !(has('termguicolors') && &termguicolors)
" Reload guard and 'compatible' handling
if exists("loaded_clrzr") | finish | endif
if !has('textprop')
echoerr 'clrzr disabled: +textprop Vim feature not found'
finish
endif
if !(has('termguicolors') && &termguicolors)
echoerr 'clrzr disabled: termguicolors not enabled and/or available'
finish
endif
if !exists('##TextChanged')
echoerr 'clrzr disabled: TextChanged autocmd not found'
finish
endif
let loaded_clrzr = 1
let s:save_cpo = &cpo
set cpo&vim
"Define commands {{{1
if !exists('g:clrzr_maxlines')
let g:clrzr_maxlines = -1
endif
command! ClrzrOn call clrzr#Enable()
command! ClrzrOff call clrzr#Disable()
command! ClrzrAposTog call clrzr#AlphaPosToggleWindow()
command! ClrzrAposTog call clrzr#AlphaPosToggle()
command! ClrzrRefresh call clrzr#Refresh()
command! ClrzrTog call clrzr#ToggleWindow()
nnoremap <leader>C :ClrzrOn<CR>
nnoremap <leader>V :ClrzrOff<CR>
nnoremap <leader>R :ClrzrRefresh<CR>
if !exists('g:clrzr_startup') || g:clrzr_startup
call clrzr#Enable()