dotfiles

Configuration for the software I use.
git clone https://git.jaderune.net/jbauer/dotfiles
Log | Files | Refs | README | LICENSE

buftabline.vim (8659B)


      1 " Vim global plugin for rendering the buffer list in the tabline
      2 " Licence:     The MIT License (MIT)
      3 " Commit:      $Format:%H$
      4 " {{{ Copyright (c) 2015 Aristotle Pagaltzis <pagaltzis@gmx.de>
      5 "
      6 " Permission is hereby granted, free of charge, to any person obtaining a copy
      7 " of this software and associated documentation files (the "Software"), to deal
      8 " in the Software without restriction, including without limitation the rights
      9 " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 " copies of the Software, and to permit persons to whom the Software is
     11 " furnished to do so, subject to the following conditions:
     12 "
     13 " The above copyright notice and this permission notice shall be included in
     14 " all copies or substantial portions of the Software.
     15 "
     16 " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19 " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 " THE SOFTWARE.
     23 " }}}
     24 
     25 if v:version < 700
     26 	echoerr printf('Vim 7 is required for buftabline (this is only %d.%d)',v:version/100,v:version%100)
     27 	finish
     28 endif
     29 
     30 scriptencoding utf-8
     31 
     32 hi default link BufTabLineCurrent         TabLineSel
     33 hi default link BufTabLineActive          PmenuSel
     34 hi default link BufTabLineHidden          TabLine
     35 hi default link BufTabLineFill            TabLineFill
     36 hi default link BufTabLineModifiedCurrent BufTabLineCurrent
     37 hi default link BufTabLineModifiedActive  BufTabLineActive
     38 hi default link BufTabLineModifiedHidden  BufTabLineHidden
     39 
     40 let g:buftabline_numbers    = get(g:, 'buftabline_numbers',    0)
     41 let g:buftabline_indicators = get(g:, 'buftabline_indicators', 0)
     42 let g:buftabline_separators = get(g:, 'buftabline_separators', 0)
     43 let g:buftabline_show       = get(g:, 'buftabline_show',       1)
     44 let g:buftabline_plug_max   = get(g:, 'buftabline_plug_max',  10)
     45 
     46 function! buftabline#user_buffers() " help buffers are always unlisted, but quickfix buffers are not
     47 	return filter(range(1,bufnr('$')),'buflisted(v:val) && "quickfix" !=? getbufvar(v:val, "&buftype")')
     48 endfunction
     49 
     50 function! s:switch_buffer(bufnum, clicks, button, mod)
     51 	execute 'buffer' a:bufnum
     52 endfunction
     53 
     54 function s:SID()
     55 	return matchstr(expand('<sfile>'), '<SNR>\d\+_')
     56 endfunction
     57 
     58 let s:dirsep = fnamemodify(getcwd(),':p')[-1:]
     59 let s:centerbuf = winbufnr(0)
     60 let s:tablineat = has('tablineat')
     61 let s:sid = s:SID() | delfunction s:SID
     62 function! buftabline#render()
     63 	let show_num = g:buftabline_numbers == 1
     64 	let show_ord = g:buftabline_numbers == 2
     65 	let show_mod = g:buftabline_indicators
     66 	let lpad     = g:buftabline_separators ? nr2char(0x23B8) : ' '
     67 
     68 	let bufnums = buftabline#user_buffers()
     69 	let centerbuf = s:centerbuf " prevent tabline jumping around when non-user buffer current (e.g. help)
     70 
     71 	" pick up data on all the buffers
     72 	let tabs = []
     73 	let path_tabs = []
     74 	let tabs_per_tail = {}
     75 	let currentbuf = winbufnr(0)
     76 	let screen_num = 0
     77 	for bufnum in bufnums
     78 		let screen_num = show_num ? bufnum : show_ord ? screen_num + 1 : ''
     79 		let tab = { 'num': bufnum, 'pre': '' }
     80 		let tab.hilite = currentbuf == bufnum ? 'Current' : bufwinnr(bufnum) > 0 ? 'Active' : 'Hidden'
     81 		if currentbuf == bufnum | let [centerbuf, s:centerbuf] = [bufnum, bufnum] | endif
     82 		let bufpath = bufname(bufnum)
     83 		if strlen(bufpath)
     84 			let tab.path = fnamemodify(bufpath, ':p:~:.')
     85 			let tab.sep = strridx(tab.path, s:dirsep, strlen(tab.path) - 2) " keep trailing dirsep
     86 			let tab.label = tab.path[tab.sep + 1:]
     87 			let pre = screen_num
     88 			if getbufvar(bufnum, '&mod')
     89 				let tab.hilite = 'Modified' . tab.hilite
     90 				if show_mod | let pre = '+' . pre | endif
     91 			endif
     92 			if strlen(pre) | let tab.pre = pre . ' ' | endif
     93 			let tabs_per_tail[tab.label] = get(tabs_per_tail, tab.label, 0) + 1
     94 			let path_tabs += [tab]
     95 		elseif -1 < index(['nofile','acwrite'], getbufvar(bufnum, '&buftype')) " scratch buffer
     96 			let tab.label = ( show_mod ? '!' . screen_num : screen_num ? screen_num . ' !' : '!' )
     97 		else " unnamed file
     98 			let tab.label = ( show_mod && getbufvar(bufnum, '&mod') ? '+' : '' )
     99 			\             . ( screen_num ? screen_num : '*' )
    100 		endif
    101 		let tabs += [tab]
    102 	endfor
    103 
    104 	" disambiguate same-basename files by adding trailing path segments
    105 	while len(filter(tabs_per_tail, 'v:val > 1'))
    106 		let [ambiguous, tabs_per_tail] = [tabs_per_tail, {}]
    107 		for tab in path_tabs
    108 			if -1 < tab.sep && has_key(ambiguous, tab.label)
    109 				let tab.sep = strridx(tab.path, s:dirsep, tab.sep - 1)
    110 				let tab.label = tab.path[tab.sep + 1:]
    111 			endif
    112 			let tabs_per_tail[tab.label] = get(tabs_per_tail, tab.label, 0) + 1
    113 		endfor
    114 	endwhile
    115 
    116 	" now keep the current buffer center-screen as much as possible:
    117 
    118 	" 1. setup
    119 	let lft = { 'lasttab':  0, 'cut':  '.', 'indicator': '<', 'width': 0, 'half': &columns / 2 }
    120 	let rgt = { 'lasttab': -1, 'cut': '.$', 'indicator': '>', 'width': 0, 'half': &columns - lft.half }
    121 
    122 	" 2. sum the string lengths for the left and right halves
    123 	let currentside = lft
    124 	let lpad_width = strwidth(lpad)
    125 	for tab in tabs
    126 		let tab.width = lpad_width + strwidth(tab.pre) + strwidth(tab.label) + 1
    127 		let tab.label = lpad . tab.pre . substitute(strtrans(tab.label), '%', '%%', 'g') . ' '
    128 		if centerbuf == tab.num
    129 			let halfwidth = tab.width / 2
    130 			let lft.width += halfwidth
    131 			let rgt.width += tab.width - halfwidth
    132 			let currentside = rgt
    133 			continue
    134 		endif
    135 		let currentside.width += tab.width
    136 	endfor
    137 	if currentside is lft " centered buffer not seen?
    138 		" then blame any overflow on the right side, to protect the left
    139 		let [lft.width, rgt.width] = [0, lft.width]
    140 	endif
    141 
    142 	" 3. toss away tabs and pieces until all fits:
    143 	if ( lft.width + rgt.width ) > &columns
    144 		let oversized
    145 		\ = lft.width < lft.half ? [ [ rgt, &columns - lft.width ] ]
    146 		\ : rgt.width < rgt.half ? [ [ lft, &columns - rgt.width ] ]
    147 		\ :                        [ [ lft, lft.half ], [ rgt, rgt.half ] ]
    148 		for [side, budget] in oversized
    149 			let delta = side.width - budget
    150 			" toss entire tabs to close the distance
    151 			while delta >= tabs[side.lasttab].width
    152 				let delta -= remove(tabs, side.lasttab).width
    153 			endwhile
    154 			" then snip at the last one to make it fit
    155 			let endtab = tabs[side.lasttab]
    156 			while delta > ( endtab.width - strwidth(strtrans(endtab.label)) )
    157 				let endtab.label = substitute(endtab.label, side.cut, '', '')
    158 			endwhile
    159 			let endtab.label = substitute(endtab.label, side.cut, side.indicator, '')
    160 		endfor
    161 	endif
    162 
    163 	if len(tabs) | let tabs[0].label = substitute(tabs[0].label, lpad, ' ', '') | endif
    164 
    165 	let swallowclicks = '%'.(1 + tabpagenr('$')).'X'
    166 	return s:tablineat
    167 		\ ? join(map(tabs,'"%#BufTabLine".v:val.hilite."#" . "%".v:val.num."@'.s:sid.'switch_buffer@" . strtrans(v:val.label)'),'') . '%#BufTabLineFill#' . swallowclicks
    168 		\ : swallowclicks . join(map(tabs,'"%#BufTabLine".v:val.hilite."#" . strtrans(v:val.label)'),'') . '%#BufTabLineFill#'
    169 endfunction
    170 
    171 function! buftabline#update(zombie)
    172 	set tabline=
    173 	if tabpagenr('$') > 1 | set guioptions+=e showtabline=2 | return | endif
    174 	set guioptions-=e
    175 	if 0 == g:buftabline_show
    176 		set showtabline=1
    177 		return
    178 	elseif 1 == g:buftabline_show
    179 		" account for BufDelete triggering before buffer is actually deleted
    180 		let bufnums = filter(buftabline#user_buffers(), 'v:val != a:zombie')
    181 		let &g:showtabline = 1 + ( len(bufnums) > 1 )
    182 	elseif 2 == g:buftabline_show
    183 		set showtabline=2
    184 	endif
    185 	set tabline=%!buftabline#render()
    186 endfunction
    187 
    188 augroup BufTabLine
    189 autocmd!
    190 autocmd VimEnter  * call buftabline#update(0)
    191 autocmd TabEnter  * call buftabline#update(0)
    192 autocmd BufAdd    * call buftabline#update(0)
    193 autocmd FileType qf call buftabline#update(0)
    194 autocmd BufDelete * call buftabline#update(str2nr(expand('<abuf>')))
    195 augroup END
    196 
    197 for s:n in range(1, g:buftabline_plug_max) + ( g:buftabline_plug_max > 0 ? [-1] : [] )
    198 	let s:b = s:n == -1 ? -1 : s:n - 1
    199 	execute printf("noremap <silent> <Plug>BufTabLine.Go(%d) :<C-U>exe 'b'.get(buftabline#user_buffers(),%d,'')<cr>", s:n, s:b)
    200 endfor
    201 unlet! s:n s:b
    202 
    203 if v:version < 703
    204 	function s:transpile()
    205 		let [ savelist, &list ] = [ &list, 0 ]
    206 		redir => src
    207 			silent function buftabline#render
    208 		redir END
    209 		let &list = savelist
    210 		let src = substitute(src, '\n\zs[0-9 ]*', '', 'g')
    211 		let src = substitute(src, 'strwidth(strtrans(\([^)]\+\)))', 'strlen(substitute(\1, ''\p\|\(.\)'', ''x\1'', ''g''))', 'g')
    212 		return src
    213 	endfunction
    214 	exe "delfunction buftabline#render\n" . s:transpile()
    215 	delfunction s:transpile
    216 endif