diff options
| author | Ben Beltran <ben@freshout.us> | 2012-10-19 15:09:38 -0500 |
|---|---|---|
| committer | Ben Beltran <ben@freshout.us> | 2012-10-19 15:09:38 -0500 |
| commit | 321ecaca67426410316db7db05e6e33809fe1e63 (patch) | |
| tree | 3811f15c160c6593e84ebfab3994c810a0eeeda5 /vim/autoload/Pl/Parser.vim | |
| parent | 9041bd938fcce0cf91032c67d529bc21fedeeed3 (diff) | |
Add powerline
Diffstat (limited to 'vim/autoload/Pl/Parser.vim')
| -rw-r--r-- | vim/autoload/Pl/Parser.vim | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/vim/autoload/Pl/Parser.vim b/vim/autoload/Pl/Parser.vim new file mode 100644 index 0000000..75c0b8f --- /dev/null +++ b/vim/autoload/Pl/Parser.vim @@ -0,0 +1,339 @@ +let g:Pl#Parser#Symbols = { + \ 'compatible': { + \ 'dividers': [ '', [0x2502], '', [0x2502] ] + \ , 'symbols' : { + \ 'BRANCH': 'BR:' + \ , 'RO' : 'RO' + \ , 'FT' : 'FT' + \ , 'LINE' : 'LN' + \ , 'COL' : 'C' + \ } + \ }, + \ 'unicode': { + \ 'dividers': [ [0x25b6], [0x276f], [0x25c0], [0x276e] ] + \ , 'symbols' : { + \ 'BRANCH': [0x26a1] + \ , 'RO' : [0x2613] + \ , 'FT' : [0x2691] + \ , 'LINE' : [0x204b] + \ , 'COL' : [0x2551] + \ }, + \ }, + \ 'fancy': { + \ 'dividers': [ [0x2b80], [0x2b81], [0x2b82], [0x2b83] ] + \ , 'symbols' : { + \ 'BRANCH': [0x2b60] + \ , 'RO' : [0x2b64] + \ , 'FT' : [0x2b62, 0x2b63] + \ , 'LINE' : [0x2b61] + \ , 'COL' : [0x2551] + \ } + \ } +\ } + +let s:LEFT_SIDE = 0 +let s:RIGHT_SIDE = 2 + +let s:PADDING = 1 + +let s:EMPTY_SEGMENT = { 'type': 'empty' } + +let s:HARD_DIVIDER = 0 +let s:SOFT_DIVIDER = 1 + +function! Pl#Parser#GetStatusline(segments) " {{{ + let statusline = { + \ 'n': '' + \ , 'N': '' + \ , 'v': '' + \ , 'i': '' + \ , 'r': '' + \ , 's': '' + \ } + + " Run through the different modes and create the statuslines + for mode in keys(statusline) + " Create an empty statusline list + let stl = [] + + call extend(stl, s:ParseSegments(mode, s:LEFT_SIDE, a:segments)) + + let statusline[mode] .= join(stl, '') + endfor + + return statusline +endfunction " }}} +function! Pl#Parser#ParseChars(arg) " {{{ + " Handles symbol arrays which can be either an array of hex values, + " or a string. Will convert the hex array to a string, or return the + " string as-is. + let arg = a:arg + + if type(arg) == type([]) + " Hex array + call map(arg, 'nr2char(v:val)') + + return join(arg, '') + endif + + " Anything else, just return it as it is + return arg +endfunction " }}} +function! s:ParseSegments(mode, side, segments, ...) " {{{ + let mode = a:mode + let side = a:side + let segments = a:segments + + let level = exists('a:1') ? a:1 : 0 + let base_color = exists('a:2') ? a:2 : {} + + let ret = [] + + for i in range(0, len(segments) - 1) + unlet! seg_prev seg_curr seg_next + + " Prepare some resources (fetch previous, current and next segment) + let seg_curr = deepcopy(get(segments, i)) + + " Find previous segment + let seg_prev = s:EMPTY_SEGMENT + + " If we're currently at i = 0 we have to start on 0 or else we will start on the last segment (list[-1]) + let range_start = (i == 0 ? i : i - 1) + for j in range(range_start, 0, -1) + let seg = deepcopy(get(segments, j)) + if get(seg, 'name') ==# 'TRUNCATE' + " Skip truncate segments + continue + endif + + " Look ahead for another segment that's visible in this mode + if index(get(seg, 'modes'), mode) != -1 + " Use this segment + let seg_prev = seg + + break + endif + endfor + + "" Find next segment + let seg_next = s:EMPTY_SEGMENT + + " If we're currently at i = len(segments) - 1 we have to start on i or else we will get an error because the index doesn't exist + let range_start = (i == len(segments) - 1 ? i : i + 1) + for j in range(range_start, len(segments) - 1, 1) + let seg = deepcopy(get(segments, j)) + if get(seg, 'name') ==# 'TRUNCATE' + " Skip truncate segments + continue + endif + + " Look ahead for another segment that's visible in this mode + if index(get(seg, 'modes'), mode) != -1 + " Use this segment + let seg_next = seg + + break + endif + endfor + + if index(get(seg_curr, 'modes', []), mode) == -1 + " The segment is invisible in this mode, skip it + " FIXME When two segments after each other are hidden, a gap appears where the segments would be, this is probably due to segment padding + continue + endif + + " Handle the different segment types + if seg_curr.type == 'segment' + if seg_curr.name ==# 'TRUNCATE' + " Truncate statusline + call add(ret, '%<') + elseif seg_curr.name ==# 'SPLIT' + " Split statusline + + " Switch sides + let side = s:RIGHT_SIDE + + " Handle highlighting + let mode_colors = get(seg_curr.colors, mode, seg_curr.colors['n']) + let hl_group = s:HlCreate(mode_colors) + + " Add segment text + call add(ret, '%#'. hl_group .'#%=') + else + " Add segment text + let text = seg_curr.text + + " Decide on whether to use the group's colors or the segment's colors + let colors = get(seg_curr, 'colors', base_color) + + " Fallback to normal (current) highlighting if we don't have mode-specific highlighting + let mode_colors = get(colors, mode, get(colors, 'n', {})) + + if empty(mode_colors) + echom 'Segment doesn''t have any colors! NS: "'. seg_curr.ns .'" SEG: "'. seg_curr.name .'"' + + continue + endif + + " Check if we're in a group (level > 0) + if level > 0 + " If we're in a group we don't have dividers between segments, so we should only pad one side + let padding_right = (side == s:LEFT_SIDE ? repeat(' ', s:PADDING) : '') + let padding_left = (side == s:RIGHT_SIDE ? repeat(' ', s:PADDING) : '') + + " Check if we lack a bg/fg color for this segment + " If we do, use the bg/fg color from base_color + let base_color_mode = ! has_key(base_color, mode) ? base_color['n'] : base_color[mode] + + for col in ['ctermbg', 'ctermfg', 'guibg', 'guifg'] + if empty(mode_colors[col]) + let mode_colors[col] = base_color_mode[col] + endif + endfor + else + "" If we're outside a group we have dividers and must pad both sides + let padding_left = repeat(' ', s:PADDING) + let padding_right = repeat(' ', s:PADDING) + endif + + " Get main hl group for segment + let hl_group = s:HlCreate(mode_colors) + + " Prepare segment text + let text = '%(%#'. hl_group .'#'. padding_left . text . padding_right . '%)' + + if level == 0 + " Add divider to single segments + let text = s:AddDivider(text, side, mode, colors, seg_prev, seg_curr, seg_next) + endif + + call add(ret, text) + endif + elseif seg_curr.type == 'segment_group' + " Recursively parse segment group + let func_params = [mode, side, seg_curr.segments, level + 1] + + if has_key(seg_curr, 'colors') + " Pass the base colors on to the child segments + call add(func_params, seg_curr.colors) + endif + + " Get segment group string + let text = join(call('s:ParseSegments', func_params), '') + + " Pad on the opposite side of the divider + let padding_right = (side == s:RIGHT_SIDE ? repeat(' ', s:PADDING) : '') + let padding_left = (side == s:LEFT_SIDE ? repeat(' ', s:PADDING) : '') + + let text = s:AddDivider(padding_left . text . padding_right, side, mode, seg_curr.colors, seg_prev, seg_curr, seg_next) + + call add(ret, text) + endif + endfor + + return ret +endfunction " }}} +function! s:HlCreate(hl) " {{{ + " Create a short and unique highlighting group name + " It uses the hex values of all the color properties and an attribute flag at the end + " NONE colors are translated to NN for cterm and NNNNNN for gui colors + let hi_group = printf('Pl%s%s%s%s%s' + \ , (a:hl['ctermfg'] == 'NONE' ? 'NN' : printf('%02x', a:hl['ctermfg'])) + \ , (a:hl['guifg'] == 'NONE' ? 'NNNNNN' : printf('%06x', a:hl['guifg'] )) + \ , (a:hl['ctermbg'] == 'NONE' ? 'NN' : printf('%02x', a:hl['ctermbg'])) + \ , (a:hl['guibg'] == 'NONE' ? 'NNNNNN' : printf('%06x', a:hl['guibg'] )) + \ , substitute(a:hl['attr'], '\v([a-zA-Z])[a-zA-Z]*,?', '\1', 'g') + \ ) + + if ! s:HlExists(hi_group) + let ctermbg = a:hl['ctermbg'] == 'NONE' ? 'NONE' : printf('%d', a:hl['ctermbg']) + if (has('win32') || has('win64')) && !has('gui_running') && ctermbg != 'NONE' && ctermbg > 128 + let ctermbg -= 128 + endif + let hi_cmd = printf('hi %s ctermfg=%s ctermbg=%s cterm=%s guifg=%s guibg=%s gui=%s' + \ , hi_group + \ , a:hl['ctermfg'] == 'NONE' ? 'NONE' : printf('%d', a:hl['ctermfg']) + \ , ctermbg + \ , a:hl['attr'] + \ , (a:hl['guifg'] == 'NONE' ? 'NONE' : printf('#%06x', a:hl['guifg'])) + \ , (a:hl['guibg'] == 'NONE' ? 'NONE' : printf('#%06x', a:hl['guibg'])) + \ , a:hl['attr'] + \ ) + + exec hi_cmd + + " Add command to Pl#HL list for caching + call add(g:Pl#HL, hi_cmd) + endif + + " Return only the highlighting group name + return hi_group +endfunction " }}} +function! s:HlExists(hl) " {{{ + if ! hlexists(a:hl) + return 0 + endif + + redir => hlstatus + silent exec 'hi' a:hl + redir END + + return (hlstatus !~ 'cleared') +endfunction " }}} +function! s:AddDivider(text, side, mode, colors, prev, curr, next) " {{{ + let seg_prev = a:prev + let seg_curr = a:curr + let seg_next = a:next + + " Set default color/type for the divider + let div_colors = get(a:colors, a:mode, a:colors['n']) + let div_type = s:SOFT_DIVIDER + + " Define segment to compare + let cmp_seg = a:side == s:LEFT_SIDE ? seg_next : seg_prev + + let cmp_all_colors = get(cmp_seg, 'colors', {}) + let cmp_colors = get(cmp_all_colors, a:mode, get(cmp_all_colors, 'n', {})) + + if ! empty(cmp_colors) + " Compare the highlighting groups + " + " If the background color for cterm is equal, use soft divider with the current segment's highlighting + " If not, use hard divider with a new highlighting group + " + " Note that if the previous/next segment is the split, a hard divider is always used + if get(div_colors, 'ctermbg') != get(cmp_colors, 'ctermbg') || get(seg_next, 'name') ==# 'SPLIT' || get(seg_prev, 'name') ==# 'SPLIT' + let div_type = s:HARD_DIVIDER + + " Create new highlighting group + " Use FG = CURRENT BG, BG = CMP BG + let div_colors['ctermfg'] = get(div_colors, 'ctermbg') + let div_colors['guifg'] = get(div_colors, 'guibg') + + let div_colors['ctermbg'] = get(cmp_colors, 'ctermbg') + let div_colors['guibg'] = get(cmp_colors, 'guibg') + + let div_colors['attr'] = 'NONE' + endif + endif + + " Prepare divider + let divider_raw = deepcopy(g:Pl#Parser#Symbols[g:Powerline_symbols].dividers[a:side + div_type]) + let divider = Pl#Parser#ParseChars(divider_raw) + + " Don't add dividers for segments adjacent to split (unless it's a hard divider) + if ((get(seg_next, 'name') ==# 'SPLIT' || get(seg_prev, 'name') ==# 'SPLIT') && div_type != s:HARD_DIVIDER) + return '' + endif + + if a:side == s:LEFT_SIDE + " Left side + " Divider to the right + return printf('%%(%s%%#%s#%s%%)', a:text, s:HlCreate(div_colors), divider) + else + " Right side + " Divider to the left + return printf('%%(%%#%s#%s%s%%)', s:HlCreate(div_colors), divider, a:text) + endif +endfunction " }}} |