aboutsummaryrefslogtreecommitdiff
path: root/vim/autoload/Pl/Parser.vim
diff options
context:
space:
mode:
authorBen Beltran <ben@freshout.us>2012-10-19 15:09:38 -0500
committerBen Beltran <ben@freshout.us>2012-10-19 15:09:38 -0500
commit321ecaca67426410316db7db05e6e33809fe1e63 (patch)
tree3811f15c160c6593e84ebfab3994c810a0eeeda5 /vim/autoload/Pl/Parser.vim
parent9041bd938fcce0cf91032c67d529bc21fedeeed3 (diff)
Add powerline
Diffstat (limited to 'vim/autoload/Pl/Parser.vim')
-rw-r--r--vim/autoload/Pl/Parser.vim339
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 " }}}