From 1bafe3d6d0925fd6dfb071f9dd2edeac658f1285 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 2 Jul 2014 01:41:43 +0900 Subject: [PATCH] Initial commit --- .travis.yml | 13 +++ README.md | 62 ++++++++++++ plugin/limelight.vim | 232 +++++++++++++++++++++++++++++++++++++++++++ test/limelight.vader | 107 ++++++++++++++++++++ 4 files changed, 414 insertions(+) create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 plugin/limelight.vim create mode 100644 test/limelight.vader diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eefe5dc --- /dev/null +++ b/.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/README.md b/README.md new file mode 100644 index 0000000..8af2490 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +limelight.vim ![travis-ci](https://travis-ci.org/junegunn/limelight.vim.svg?branch=master) +============= + +![](https://cloud.githubusercontent.com/assets/700826/3441009/00383ac0-00ff-11e4-81f1-5873e4c3e4e9.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 + +### 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 +``` + +Goyo.vim integration +-------------------- + +```vim +function! GoyoBefore() + Limelight +endfunction + +function! GoyoAfter() + Limelight! +endfunction + +let g:goyo_callbacks = [function('GoyoBefore'), function('GoyoAfter')] +``` + +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/plugin/limelight.vim b/plugin/limelight.vim new file mode 100644 index 0000000..5ebe2ea --- /dev/null +++ b/plugin/limelight.vim @@ -0,0 +1,232 @@ +" Copyright (c) 2014 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 = 0.5 +let s:unsupported = + \ 'Unsupported color scheme. g:limelight_conceal_' . + \ (has('gui_running') ? 'gui' : 'cterm') . 'fg required.' +let s:invalid_coefficient = 'Invalid coefficient. Expected: 0.0 ~ 1.0' + +function! s:limelight() + if !exists('w:limelight_prev') + let w:limelight_prev = [0, 0, 0, 0] + endif + if !exists('w:limelight_match_ids') + let w:limelight_match_ids = [] + endif + + let curr = [line('.'), line('$')] + if curr ==# w:limelight_prev[0 : 1] + return + endif + + let paragraph = [searchpos('^$', 'bnW')[0], searchpos('^$', 'nW')[0]] + if paragraph ==# w:limelight_prev[2 : 3] + return + endif + + call s:clear_hl() + call add(w:limelight_match_ids, matchadd('LimelightDim', '\%<'.paragraph[0].'l')) + if paragraph[1] > 0 + call add(w:limelight_match_ids, matchadd('LimelightDim', '\%>'.paragraph[1].'l')) + endif + let w:limelight_prev = extend(curr, paragraph) +endfunction + +function! s:clear_hl() + while !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 + +function! s:gray_contiguous(col) + let val = (a:col == 15 || a:col == 231) ? 256 : (a:col == 0 ? 231 : 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') + 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', 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(...) + 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 + + augroup limelight + autocmd! + autocmd CursorMoved,CursorMovedI * call s:limelight() + autocmd ColorScheme * try + \| call s:dim(s:limelight_coeff) + \| call s:limelight() + \| 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() + if exists('w:limelight_match_ids') + call s:clear_hl() + endif + augroup limelight + autocmd! + augroup END + augroup! limelight + unlet! w:limelight_prev w:limelight_match_ids +endfunction + +function! s:is_on() + return exists('#limelight') +endfunction + +function! s:do(bang, ...) + if a:bang + if a:0 > 0 && a:1 =~ '^!' && !s:is_on() + if len(a:1) > 1 + call s:on(a:1[1:-1]) + else + call s:on() + endif + else + call s:off() + endif + elseif a:0 > 0 + call s:on(a:1) + else + call s:on() + endif +endfunction + +function! s:cleanup() + if !s:is_on() && exists('w:limelight_match_ids') + call s:clear_hl() + end +endfunction + +command! -nargs=? -bar -bang Limelight call s:do('' == '!', ) + +let &cpo = s:cpo_save +unlet s:cpo_save + diff --git a/test/limelight.vader b/test/limelight.vader new file mode 100644 index 0000000..ae59c3f --- /dev/null +++ b/test/limelight.vader @@ -0,0 +1,107 @@ +Execute (Setup): + colo seoul256 + +Before: + unlet! g:limelight_default_coefficient + \ g:limelight_conceal_guifg g:limelight_conceal_ctermfg + +Execute (Invalid argument type): + Limelight 'hello' + Assert !exists('#limelight') + + Limelight {} + Assert !exists('#limelight') + + Limelight [] + Assert !exists('#limelight') + +Execute (Invalid argument range): + Limelight 1.2 + Assert !exists('#limelight') + + Limelight 2 + Assert !exists('#limelight') + + Limelight -0.2 + Assert !exists('#limelight') + + Limelight -1 + Assert !exists('#limelight') + +Execute (Limelight / Limelight!): + Limelight + Assert exists('#limelight') + + Limelight + Assert exists('#limelight') + + Limelight! + Assert !exists('#limelight') + + Limelight! + Assert !exists('#limelight') + + Limelight 0.5 + Assert exists('#limelight') + + Limelight! + Assert !exists('#limelight') + +Execute (Limelight!!): + Limelight!! + Assert exists('#limelight') + + Limelight!! + Assert !exists('#limelight') + + Limelight!! 2.0 + Assert !exists('#limelight') + + Limelight!! 0.9 + Assert exists('#limelight') + + Limelight!! 0.9 + Assert !exists('#limelight') + +Execute (g:limelight_default_coefficient): + let g:limelight_default_coefficient = 2.0 + Limelight + Assert !exists('#limelight') + + let g:limelight_default_coefficient = -1.0 + Limelight + Assert !exists('#limelight') + +Execute (Unsupported terminal): + colo default + let &t_Co = 16 + + Limelight + Assert !exists('#limelight') + +Execute (Unsupported. but g:limelight_conceal_???fg defined): + if has('gui_running') + colo default + let g:limelight_conceal_guifg = '#cccccc' + Limelight + Assert exists('#limelight') + Limelight! + Assert !exists('#limelight') + else + colo default + let &t_Co = 256 + let g:limelight_conceal_ctermfg = 240 + + Limelight + Assert exists('#limelight') + Limelight! + Assert !exists('#limelight') + + let g:limelight_conceal_ctermfg = 'red' + + endif + + " However, you're still not allowed to pass argument to Limelight command + Limelight 0.5 + Assert !exists('#limelight') +