From 071616d207ed36e9355b7be7b50da9bc8adffc16 Mon Sep 17 00:00:00 2001 From: Anthony Perkins Date: Mon, 10 Jun 2019 20:40:44 +0100 Subject: [PATCH] Add limelight.vim 106fb5749d227a0de72e36068ed72798c6fd48e6 --- pack/acp/README.md | 1 + pack/acp/start/limelight.vim/.travis.yml | 13 + pack/acp/start/limelight.vim/README.md | 80 +++++ .../limelight.vim/autoload/limelight.vim | 282 ++++++++++++++++++ .../start/limelight.vim/plugin/limelight.vim | 27 ++ .../start/limelight.vim/test/limelight.vader | 136 +++++++++ 6 files changed, 539 insertions(+) create mode 100644 pack/acp/start/limelight.vim/.travis.yml create mode 100644 pack/acp/start/limelight.vim/README.md create mode 100644 pack/acp/start/limelight.vim/autoload/limelight.vim create mode 100644 pack/acp/start/limelight.vim/plugin/limelight.vim create mode 100644 pack/acp/start/limelight.vim/test/limelight.vader diff --git a/pack/acp/README.md b/pack/acp/README.md index 878e340..282b61b 100644 --- a/pack/acp/README.md +++ b/pack/acp/README.md @@ -6,6 +6,7 @@ Git submodules are slow, so handle this manually. * [start/goyo.vim](https://github.com/junegunn/goyo.vim) * [start/gruvbox](https://github.com/morhetz/gruvbox) * [start/jellybeans.vim](https://github.com/nanotech/jellybeans.vim) +* [start/limelight.vim](https://github.com/junegunn/limelight.vim) * [start/minimalist](https://github.com/dikiaap/minimalist) * [start/mom.vim](https://github.com/vim-scripts/mom.vim) * [start/rust.vim](https://github.com/rust-lang/rust.vim) diff --git a/pack/acp/start/limelight.vim/.travis.yml b/pack/acp/start/limelight.vim/.travis.yml new file mode 100644 index 0000000..eefe5dc --- /dev/null +++ b/pack/acp/start/limelight.vim/.travis.yml @@ -0,0 +1,13 @@ +language: vim + +before_script: | + git clone https://github.com/junegunn/vader.vim.git + git clone https://github.com/junegunn/seoul256.vim.git + +script: | + vim -Nu <(cat << VIMRC + set rtp+=vader.vim + set rtp+=seoul256.vim + set rtp+=. + VIMRC) -c 'silent Vader! test/*' > /dev/null + diff --git a/pack/acp/start/limelight.vim/README.md b/pack/acp/start/limelight.vim/README.md new file mode 100644 index 0000000..4a76d81 --- /dev/null +++ b/pack/acp/start/limelight.vim/README.md @@ -0,0 +1,80 @@ +limelight.vim ![travis-ci](https://travis-ci.org/junegunn/limelight.vim.svg?branch=master) +============= + +Hyperfocus-writing in Vim. + +![](https://raw.github.com/junegunn/i/master/limelight.gif) + +Best served with [Goyo.vim](https://github.com/junegunn/goyo.vim). +Works on 256-color terminal or on GVim. + +Usage +----- + +- `Limelight [0.0 ~ 1.0]` + - Turn Limelight on +- `Limelight!` + - Turn Limelight off +- `Limelight!! [0.0 ~ 1.0]` + - Toggle Limelight + +### Limelight for a selected range + +You can invoke `:Limelight` for a visual range. There are also `` +mappings for normal and visual mode for the purpose. + +```vim +nmap l (Limelight) +xmap l (Limelight) +``` + +### Options + +For some color schemes, Limelight may not be able to calculate the color for +dimming down the surrounding paragraphs. In that case, you need to define +`g:limelight_conceal_ctermfg` or `g:limelight_conceal_guifg`. + +```vim +" Color name (:help cterm-colors) or ANSI code +let g:limelight_conceal_ctermfg = 'gray' +let g:limelight_conceal_ctermfg = 240 + +" Color name (:help gui-colors) or RGB color +let g:limelight_conceal_guifg = 'DarkGray' +let g:limelight_conceal_guifg = '#777777' + +" Default: 0.5 +let g:limelight_default_coefficient = 0.7 + +" Number of preceding/following paragraphs to include (default: 0) +let g:limelight_paragraph_span = 1 + +" Beginning/end of paragraph +" When there's no empty line between the paragraphs +" and each paragraph starts with indentation +let g:limelight_bop = '^\s' +let g:limelight_eop = '\ze\n^\s' + +" Highlighting priority (default: 10) +" Set it to -1 not to overrule hlsearch +let g:limelight_priority = -1 +``` + +Goyo.vim integration +-------------------- + +```vim +autocmd! User GoyoEnter Limelight +autocmd! User GoyoLeave Limelight! +``` + +Acknowledgement +--------------- + +Thanks to [@Cutuchiqueno](https://github.com/Cutuchiqueno) for [suggesting +the idea](https://github.com/junegunn/goyo.vim/issues/34). + +License +------- + +MIT diff --git a/pack/acp/start/limelight.vim/autoload/limelight.vim b/pack/acp/start/limelight.vim/autoload/limelight.vim new file mode 100644 index 0000000..f740f2d --- /dev/null +++ b/pack/acp/start/limelight.vim/autoload/limelight.vim @@ -0,0 +1,282 @@ +" Copyright (c) 2015 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_limelight') + finish +endif +let g:loaded_limelight = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:default_coeff = str2float('0.5') +let s:invalid_coefficient = 'Invalid coefficient. Expected: 0.0 ~ 1.0' + +function! s:unsupported() + let var = 'g:limelight_conceal_'.(has('gui_running') ? 'gui' : 'cterm').'fg' + + if exists(var) + return 'Cannot calculate background color.' + else + return 'Unsupported color scheme. '.var.' required.' + endif +endfunction + +function! s:getpos() + let bop = get(g:, 'limelight_bop', '^\s*$\n\zs') + let eop = get(g:, 'limelight_eop', '^\s*$') + let span = max([0, get(g:, 'limelight_paragraph_span', 0) - s:empty(getline('.'))]) + let pos = getpos('.') + for i in range(0, span) + let start = searchpos(bop, i == 0 ? 'cbW' : 'bW')[0] + endfor + call setpos('.', pos) + for _ in range(0, span) + let end = searchpos(eop, 'W')[0] + endfor + call setpos('.', pos) + return [start, end] +endfunction + +function! s:empty(line) + return (a:line =~# '^\s*$') +endfunction + +function! s:limelight() + if !empty(get(w:, 'limelight_range', [])) + return + endif + if !exists('w:limelight_prev') + let w:limelight_prev = [0, 0, 0, 0] + endif + + let curr = [line('.'), line('$')] + if curr ==# w:limelight_prev[0 : 1] + return + endif + + let paragraph = s:getpos() + if paragraph ==# w:limelight_prev[2 : 3] + return + endif + + call s:clear_hl() + call call('s:hl', paragraph) + let w:limelight_prev = extend(curr, paragraph) +endfunction + +function! s:hl(startline, endline) + let w:limelight_match_ids = get(w:, 'limelight_match_ids', []) + let priority = get(g:, 'limelight_priority', 10) + call add(w:limelight_match_ids, matchadd('LimelightDim', '\%<'.a:startline.'l', priority)) + if a:endline > 0 + call add(w:limelight_match_ids, matchadd('LimelightDim', '\%>'.a:endline.'l', priority)) + endif +endfunction + +function! s:clear_hl() + while exists('w:limelight_match_ids') && !empty(w:limelight_match_ids) + silent! call matchdelete(remove(w:limelight_match_ids, -1)) + endwhile +endfunction + +function! s:hex2rgb(str) + let str = substitute(a:str, '^#', '', '') + return [eval('0x'.str[0:1]), eval('0x'.str[2:3]), eval('0x'.str[4:5])] +endfunction + +let s:gray_converter = { +\ 0: 231, +\ 7: 254, +\ 15: 256, +\ 16: 231, +\ 231: 256 +\ } + +function! s:gray_contiguous(col) + let val = get(s:gray_converter, a:col, a:col) + if val < 231 || val > 256 + throw s:unsupported() + endif + return val +endfunction + +function! s:gray_ansi(col) + return a:col == 231 ? 0 : (a:col == 256 ? 231 : a:col) +endfunction + +function! s:coeff(coeff) + let coeff = a:coeff < 0 ? + \ get(g:, 'limelight_default_coefficient', s:default_coeff) : a:coeff + if coeff < 0 || coeff > 1 + throw 'Invalid g:limelight_default_coefficient. Expected: 0.0 ~ 1.0' + endif + return coeff +endfunction + +function! s:dim(coeff) + let synid = synIDtrans(hlID('Normal')) + let fg = synIDattr(synid, 'fg#') + let bg = synIDattr(synid, 'bg#') + + if has('gui_running') || has('termguicolors') && &termguicolors || has('nvim') && $NVIM_TUI_ENABLE_TRUE_COLOR + if a:coeff < 0 && exists('g:limelight_conceal_guifg') + let dim = g:limelight_conceal_guifg + elseif empty(fg) || empty(bg) + throw s:unsupported() + else + let coeff = s:coeff(a:coeff) + let fg_rgb = s:hex2rgb(fg) + let bg_rgb = s:hex2rgb(bg) + let dim_rgb = [ + \ bg_rgb[0] * coeff + fg_rgb[0] * (1 - coeff), + \ bg_rgb[1] * coeff + fg_rgb[1] * (1 - coeff), + \ bg_rgb[2] * coeff + fg_rgb[2] * (1 - coeff)] + let dim = '#'.join(map(dim_rgb, 'printf("%x", float2nr(v:val))'), '') + endif + execute printf('hi LimelightDim guifg=%s guisp=bg', dim) + elseif &t_Co == 256 + if a:coeff < 0 && exists('g:limelight_conceal_ctermfg') + let dim = g:limelight_conceal_ctermfg + elseif fg <= -1 || bg <= -1 + throw s:unsupported() + else + let coeff = s:coeff(a:coeff) + let fg = s:gray_contiguous(fg) + let bg = s:gray_contiguous(bg) + let dim = s:gray_ansi(float2nr(bg * coeff + fg * (1 - coeff))) + endif + if type(dim) == 1 + execute printf('hi LimelightDim ctermfg=%s', dim) + else + execute printf('hi LimelightDim ctermfg=%d', dim) + endif + else + throw 'Unsupported terminal. Sorry.' + endif +endfunction + +function! s:error(msg) + echohl ErrorMsg + echo a:msg + echohl None +endfunction + +function! s:parse_coeff(coeff) + let t = type(a:coeff) + if t == 1 + if a:coeff =~ '^ *[0-9.]\+ *$' + let c = str2float(a:coeff) + else + throw s:invalid_coefficient + endif + elseif index([0, 5], t) >= 0 + let c = t + else + throw s:invalid_coefficient + endif + return c +endfunction + +function! s:on(range, ...) + try + let s:limelight_coeff = a:0 > 0 ? s:parse_coeff(a:1) : -1 + call s:dim(s:limelight_coeff) + catch + return s:error(v:exception) + endtry + + let w:limelight_range = a:range + if !empty(a:range) + call s:clear_hl() + call call('s:hl', a:range) + endif + + augroup limelight + let was_on = exists('#limelight#CursorMoved') + autocmd! + if empty(a:range) || was_on + autocmd CursorMoved,CursorMovedI * call s:limelight() + endif + autocmd ColorScheme * try + \| call s:dim(s:limelight_coeff) + \| catch + \| call s:off() + \| throw v:exception + \| endtry + augroup END + + " FIXME: We cannot safely remove this group once Limelight started + augroup limelight_cleanup + autocmd! + autocmd WinEnter * call s:cleanup() + augroup END + + doautocmd CursorMoved +endfunction + +function! s:off() + call s:clear_hl() + augroup limelight + autocmd! + augroup END + augroup! limelight + unlet! w:limelight_prev w:limelight_match_ids w:limelight_range +endfunction + +function! s:is_on() + return exists('#limelight') +endfunction + +function! s:cleanup() + if !s:is_on() + call s:clear_hl() + end +endfunction + +function! limelight#execute(bang, visual, ...) range + let range = a:visual ? [a:firstline, a:lastline] : [] + if a:bang + if a:0 > 0 && a:1 =~ '^!' && !s:is_on() + if len(a:1) > 1 + call s:on(range, a:1[1:-1]) + else + call s:on(range) + endif + else + call s:off() + endif + elseif a:0 > 0 + call s:on(range, a:1) + else + call s:on(range) + endif +endfunction + +function! limelight#operator(...) + '[,']call limelight#execute(0, 1) +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + diff --git a/pack/acp/start/limelight.vim/plugin/limelight.vim b/pack/acp/start/limelight.vim/plugin/limelight.vim new file mode 100644 index 0000000..427e54e --- /dev/null +++ b/pack/acp/start/limelight.vim/plugin/limelight.vim @@ -0,0 +1,27 @@ +" Copyright (c) 2015 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +command! -nargs=? -bar -bang -range Limelight ,call limelight#execute(0, > 0, ) + +nnoremap (Limelight) :set opfunc=limelight#operatorg@ +xnoremap (Limelight) :Limelight diff --git a/pack/acp/start/limelight.vim/test/limelight.vader b/pack/acp/start/limelight.vim/test/limelight.vader new file mode 100644 index 0000000..b3b7519 --- /dev/null +++ b/pack/acp/start/limelight.vim/test/limelight.vader @@ -0,0 +1,136 @@ +Execute (Setup): + Save g:colors_name, &background + colo seoul256 + + function! s:assert_on() + Assert exists('#limelight'), 'limelight should be on' + endfunction + function! s:assert_off() + Assert !exists('#limelight'), 'limelight should be off' + endfunction + command! AssertOn call s:assert_on() + command! AssertOff call s:assert_off() + +Before: + unlet! g:limelight_default_coefficient + \ g:limelight_conceal_guifg g:limelight_conceal_ctermfg + +Execute (Invalid argument type): + Limelight 'hello' + AssertOff + + Limelight {} + AssertOff + + Limelight [] + AssertOff + +Execute (Invalid argument range): + Limelight 1.2 + AssertOff + + Limelight 2 + AssertOff + + Limelight -0.2 + AssertOff + + Limelight -1 + AssertOff + +Execute (Limelight / Limelight!): + Limelight + AssertOn + + Limelight + AssertOn + + Limelight! + AssertOff + + Limelight! + AssertOff + + Limelight 0.5 + AssertOn + + Limelight! + AssertOff + +Execute (Limelight!!): + Limelight!! + AssertOn + + Limelight!! + AssertOff + + Limelight!! 2.0 + AssertOff + + Limelight!! 0.9 + AssertOn + + Limelight!! 0.9 + AssertOff + +Execute (g:limelight_default_coefficient): + let g:limelight_default_coefficient = 2.0 + Limelight + AssertOff + + let g:limelight_default_coefficient = -1.0 + Limelight + AssertOff + +Execute (Unsupported terminal): + colo default + let &t_Co = 16 + + Limelight + AssertOff + +Execute (Unsupported. but g:limelight_conceal_???fg defined): + if has('gui_running') + colo default + let g:limelight_conceal_guifg = '#cccccc' + Limelight + AssertOn + Limelight! + AssertOff + else + colo default + let &t_Co = 256 + + " Cannot enable it without g:limelight_conceal_ctermfg + Limelight + AssertOff + + let g:limelight_conceal_ctermfg = 240 + Limelight + AssertOn + Limelight! + AssertOff + + let g:limelight_conceal_ctermfg = 'red' + Limelight + AssertOn + Limelight! + AssertOff + endif + + " However, you're still not allowed to pass argument to Limelight command + redir => msg + Limelight 0.5 + redir END + AssertOff + Assert msg !~ 'g:limelight_conceal_', 'Invalid message: '.msg + +Execute (TODO: Color interpolation): + Assert 0, 'TODO' + +Execute (Cleanup): + delc AssertOn + delc AssertOff + unlet! msg + Restore + execute 'colo '.g:colors_name