diff --git a/README.md b/README.md index 3820048..0f2f369 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/autoload/clrzr.awk b/autoload/clrzr.awk index cb64e2e..f85cf73 100644 --- a/autoload/clrzr.awk +++ b/autoload/clrzr.awk @@ -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) } } diff --git a/autoload/clrzr.vim b/autoload/clrzr.vim index 3689426..8cf5639 100644 --- a/autoload/clrzr.vim +++ b/autoload/clrzr.vim @@ -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', - \ [ ' 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', - \ [ ' -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(':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 diff --git a/doc/clrzr.txt b/doc/clrzr.txt index 7cd4a68..8c89dce 100644 --- a/doc/clrzr.txt +++ b/doc/clrzr.txt @@ -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: diff --git a/doc/tags b/doc/tags index 8a37a2c..01f8ac7 100644 --- a/doc/tags +++ b/doc/tags @@ -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* diff --git a/plugin/clrzr.vim b/plugin/clrzr.vim index b8802ed..3fd478a 100644 --- a/plugin/clrzr.vim +++ b/plugin/clrzr.vim @@ -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 C :ClrzrOn +nnoremap V :ClrzrOff +nnoremap R :ClrzrRefresh if !exists('g:clrzr_startup') || g:clrzr_startup call clrzr#Enable()