PageRenderTime 24ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/dot/vim/bundle/vim-airline/autoload/airline/extensions/branch.vim

https://github.com/thomasritz/dotfiles
Vim Script | 349 lines | 271 code | 32 blank | 46 comment | 53 complexity | e33de0405cc158324caadfb2244b9d6e MD5 | raw file
Possible License(s): AGPL-3.0, WTFPL, BSD-3-Clause
  1. " MIT License. Copyright (c) 2013-2020 Bailey Ling et al.
  2. " Plugin: fugitive, gina, lawrencium and vcscommand
  3. " vim: et ts=2 sts=2 sw=2
  4. scriptencoding utf-8
  5. " s:vcs_config contains static configuration of VCSes and their status relative
  6. " to the active file.
  7. " 'branch' - The name of currently active branch. This field is empty iff it
  8. " has not been initialized yet or the current file is not in
  9. " an active branch.
  10. " 'untracked' - Cache of untracked files represented as a dictionary with files
  11. " as keys. A file has a not exists symbol set as its value if it
  12. " is untracked. A file is present in this dictionary iff its
  13. " status is considered up to date.
  14. " 'untracked_mark' - used as regexp to test against the output of 'cmd'
  15. let s:vcs_config = {
  16. \ 'git': {
  17. \ 'exe': 'git',
  18. \ 'cmd': 'git status --porcelain -- ',
  19. \ 'dirty': 'git status -uno --porcelain --ignore-submodules',
  20. \ 'untracked_mark': '??',
  21. \ 'exclude': '\.git',
  22. \ 'update_branch': 's:update_git_branch',
  23. \ 'display_branch': 's:display_git_branch',
  24. \ 'branch': '',
  25. \ 'untracked': {},
  26. \ },
  27. \ 'mercurial': {
  28. \ 'exe': 'hg',
  29. \ 'cmd': 'hg status -u -- ',
  30. \ 'dirty': 'hg status -mard',
  31. \ 'untracked_mark': '?',
  32. \ 'exclude': '\.hg',
  33. \ 'update_branch': 's:update_hg_branch',
  34. \ 'display_branch': 's:display_hg_branch',
  35. \ 'branch': '',
  36. \ 'untracked': {},
  37. \ },
  38. \}
  39. " Initializes b:buffer_vcs_config. b:buffer_vcs_config caches the branch and
  40. " untracked status of the file in the buffer. Caching those fields is necessary,
  41. " because s:vcs_config may be updated asynchronously and s:vcs_config fields may
  42. " be invalid during those updates. b:buffer_vcs_config fields are updated
  43. " whenever corresponding fields in s:vcs_config are updated or an inconsistency
  44. " is detected during update_* operation.
  45. "
  46. " b:airline_head caches the head string it is empty iff it needs to be
  47. " recalculated. b:airline_head is recalculated based on b:buffer_vcs_config.
  48. function! s:init_buffer()
  49. let b:buffer_vcs_config = {}
  50. for vcs in keys(s:vcs_config)
  51. let b:buffer_vcs_config[vcs] = {
  52. \ 'branch': '',
  53. \ 'untracked': '',
  54. \ 'dirty': 0,
  55. \ }
  56. endfor
  57. unlet! b:airline_head
  58. endfunction
  59. let s:head_format = get(g:, 'airline#extensions#branch#format', 0)
  60. if s:head_format == 1
  61. function! s:format_name(name)
  62. return fnamemodify(a:name, ':t')
  63. endfunction
  64. elseif s:head_format == 2
  65. function! s:format_name(name)
  66. return pathshorten(a:name)
  67. endfunction
  68. elseif type(s:head_format) == type('')
  69. function! s:format_name(name)
  70. return call(s:head_format, [a:name])
  71. endfunction
  72. else
  73. function! s:format_name(name)
  74. return a:name
  75. endfunction
  76. endif
  77. " Fugitive special revisions. call '0' "staging" ?
  78. let s:names = {'0': 'index', '1': 'orig', '2':'fetch', '3':'merge'}
  79. let s:sha1size = get(g:, 'airline#extensions#branch#sha1_len', 7)
  80. function! s:update_git_branch()
  81. call airline#util#ignore_next_focusgain()
  82. if !airline#util#has_fugitive() && !airline#util#has_gina()
  83. let s:vcs_config['git'].branch = ''
  84. return
  85. endif
  86. if airline#util#has_fugitive()
  87. let s:vcs_config['git'].branch = exists("*FugitiveHead") ?
  88. \ FugitiveHead(s:sha1size) : fugitive#head(s:sha1size)
  89. if s:vcs_config['git'].branch is# 'master' &&
  90. \ airline#util#winwidth() < 81
  91. " Shorten default a bit
  92. let s:vcs_config['git'].branch='mas'
  93. endif
  94. else
  95. try
  96. let g:gina#component#repo#commit_length = s:sha1size
  97. let s:vcs_config['git'].branch = gina#component#repo#branch()
  98. catch
  99. endtry
  100. if s:vcs_config['git'].branch is# 'master' &&
  101. \ airline#util#winwidth() < 81
  102. " Shorten default a bit
  103. let s:vcs_config['git'].branch='mas'
  104. endif
  105. endif
  106. endfunction
  107. function! s:display_git_branch()
  108. " disable FocusGained autocommand, might cause loops because system() causes
  109. " a refresh, which causes a system() command again #2029
  110. call airline#util#ignore_next_focusgain()
  111. let name = b:buffer_vcs_config['git'].branch
  112. try
  113. let commit = matchstr(FugitiveParse()[0], '^\x\+')
  114. if has_key(s:names, commit)
  115. let name = get(s:names, commit)."(".name.")"
  116. elseif !empty(commit)
  117. let ref = fugitive#repo().git_chomp('describe', '--all', '--exact-match', commit)
  118. if ref !~ "^fatal: no tag exactly matches"
  119. let name = s:format_name(substitute(ref, '\v\C^%(heads/|remotes/|tags/)=','',''))."(".name.")"
  120. else
  121. let name = matchstr(commit, '.\{'.s:sha1size.'}')."(".name.")"
  122. endif
  123. endif
  124. catch
  125. endtry
  126. return name
  127. endfunction
  128. function! s:update_hg_branch()
  129. if airline#util#has_lawrencium()
  130. let cmd='LC_ALL=C hg qtop'
  131. let stl=lawrencium#statusline()
  132. let file=expand('%:p')
  133. if !empty(stl) && get(b:, 'airline_do_mq_check', 1)
  134. if g:airline#init#vim_async
  135. noa call airline#async#get_mq_async(cmd, file)
  136. elseif has("nvim")
  137. noa call airline#async#nvim_get_mq_async(cmd, file)
  138. else
  139. " remove \n at the end of the command
  140. let output=system(cmd)[0:-2]
  141. noa call airline#async#mq_output(output, file)
  142. endif
  143. endif
  144. " do not do mq check anymore
  145. let b:airline_do_mq_check = 0
  146. if exists("b:mq") && !empty(b:mq)
  147. if stl is# 'default'
  148. " Shorten default a bit
  149. let stl='def'
  150. endif
  151. let stl.=' ['.b:mq.']'
  152. endif
  153. let s:vcs_config['mercurial'].branch = stl
  154. else
  155. let s:vcs_config['mercurial'].branch = ''
  156. endif
  157. endfunction
  158. function! s:display_hg_branch()
  159. return b:buffer_vcs_config['mercurial'].branch
  160. endfunction
  161. function! s:update_branch()
  162. for vcs in keys(s:vcs_config)
  163. call {s:vcs_config[vcs].update_branch}()
  164. if b:buffer_vcs_config[vcs].branch != s:vcs_config[vcs].branch
  165. let b:buffer_vcs_config[vcs].branch = s:vcs_config[vcs].branch
  166. unlet! b:airline_head
  167. endif
  168. endfor
  169. endfunction
  170. function! airline#extensions#branch#update_untracked_config(file, vcs)
  171. if !has_key(s:vcs_config[a:vcs].untracked, a:file)
  172. return
  173. elseif s:vcs_config[a:vcs].untracked[a:file] != b:buffer_vcs_config[a:vcs].untracked
  174. let b:buffer_vcs_config[a:vcs].untracked = s:vcs_config[a:vcs].untracked[a:file]
  175. unlet! b:airline_head
  176. endif
  177. endfunction
  178. function! s:update_untracked()
  179. let file = expand("%:p")
  180. if empty(file) || isdirectory(file) || !empty(&buftype)
  181. return
  182. endif
  183. let needs_update = 1
  184. let vcs_checks = get(g:, "airline#extensions#branch#vcs_checks", ["untracked", "dirty"])
  185. for vcs in keys(s:vcs_config)
  186. if file =~ s:vcs_config[vcs].exclude
  187. " Skip check for files that live in the exclude directory
  188. let needs_update = 0
  189. endif
  190. if has_key(s:vcs_config[vcs].untracked, file)
  191. let needs_update = 0
  192. call airline#extensions#branch#update_untracked_config(file, vcs)
  193. endif
  194. endfor
  195. if !needs_update
  196. return
  197. endif
  198. for vcs in keys(s:vcs_config)
  199. " only check, for git, if fugitive is installed
  200. " and for 'hg' if lawrencium is installed, else skip
  201. if vcs is# 'git' && (!airline#util#has_fugitive() && !airline#util#has_gina())
  202. continue
  203. elseif vcs is# 'mercurial' && !airline#util#has_lawrencium()
  204. continue
  205. endif
  206. let config = s:vcs_config[vcs]
  207. " Note that asynchronous update updates s:vcs_config only, and only
  208. " s:update_untracked updates b:buffer_vcs_config. If s:vcs_config is
  209. " invalidated again before s:update_untracked is called, then we lose the
  210. " result of the previous call, i.e. the head string is not updated. It
  211. " doesn't happen often in practice, so we let it be.
  212. if index(vcs_checks, 'untracked') > -1
  213. call airline#async#vcs_untracked(config, file, vcs)
  214. endif
  215. " Check clean state of repo
  216. if index(vcs_checks, 'dirty') > -1
  217. call airline#async#vcs_clean(config.dirty, file, vcs)
  218. endif
  219. endfor
  220. endfunction
  221. function! airline#extensions#branch#head()
  222. if !exists('b:buffer_vcs_config')
  223. call s:init_buffer()
  224. endif
  225. call s:update_branch()
  226. call s:update_untracked()
  227. if exists('b:airline_head') && !empty(b:airline_head)
  228. return b:airline_head
  229. endif
  230. let b:airline_head = ''
  231. let vcs_priority = get(g:, "airline#extensions#branch#vcs_priority", ["git", "mercurial"])
  232. let heads = []
  233. for vcs in vcs_priority
  234. if !empty(b:buffer_vcs_config[vcs].branch)
  235. let heads += [vcs]
  236. endif
  237. endfor
  238. for vcs in heads
  239. if !empty(b:airline_head)
  240. let b:airline_head .= ' | '
  241. endif
  242. if len(heads) > 1
  243. let b:airline_head .= s:vcs_config[vcs].exe .':'
  244. endif
  245. let b:airline_head .= s:format_name({s:vcs_config[vcs].display_branch}())
  246. let additional = b:buffer_vcs_config[vcs].untracked
  247. if empty(additional) &&
  248. \ has_key(b:buffer_vcs_config[vcs], 'dirty') &&
  249. \ b:buffer_vcs_config[vcs].dirty
  250. let additional = g:airline_symbols['dirty']
  251. endif
  252. let b:airline_head .= additional
  253. endfor
  254. if empty(heads)
  255. if airline#util#has_vcscommand()
  256. noa call VCSCommandEnableBufferSetup()
  257. if exists('b:VCSCommandBufferInfo')
  258. let b:airline_head = s:format_name(get(b:VCSCommandBufferInfo, 0, ''))
  259. endif
  260. endif
  261. endif
  262. if empty(heads)
  263. if airline#util#has_custom_scm()
  264. try
  265. let Fn = function(g:airline#extensions#branch#custom_head)
  266. let b:airline_head = Fn()
  267. endtry
  268. endif
  269. endif
  270. if exists("g:airline#extensions#branch#displayed_head_limit")
  271. let w:displayed_head_limit = g:airline#extensions#branch#displayed_head_limit
  272. if strwidth(b:airline_head) > w:displayed_head_limit - 1
  273. let b:airline_head =
  274. \ airline#util#strcharpart(b:airline_head, 0, w:displayed_head_limit - 1)
  275. \ . (&encoding ==? 'utf-8' ? '…' : '.')
  276. endif
  277. endif
  278. return b:airline_head
  279. endfunction
  280. function! airline#extensions#branch#get_head()
  281. let head = airline#extensions#branch#head()
  282. let winwidth = get(airline#parts#get('branch'), 'minwidth', 120)
  283. let minwidth = empty(get(b:, 'airline_hunks', '')) ? 14 : 7
  284. let head = airline#util#shorten(head, winwidth, minwidth)
  285. let symbol = get(g:, 'airline#extensions#branch#symbol', g:airline_symbols.branch)
  286. return empty(head)
  287. \ ? get(g:, 'airline#extensions#branch#empty_message', '')
  288. \ : printf('%s%s', empty(symbol) ? '' : symbol.(g:airline_symbols.space), head)
  289. endfunction
  290. function! s:reset_untracked_cache(shellcmdpost)
  291. " shellcmdpost - whether function was called as a result of ShellCmdPost hook
  292. if !g:airline#init#vim_async && !has('nvim')
  293. if a:shellcmdpost
  294. " Clear cache only if there was no error or the script uses an
  295. " asynchronous interface. Otherwise, cache clearing would overwrite
  296. " v:shell_error with a system() call inside get_*_untracked.
  297. if v:shell_error
  298. return
  299. endif
  300. endif
  301. endif
  302. let file = expand("%:p")
  303. for vcs in keys(s:vcs_config)
  304. " Dump the value of the cache for the current file. Partially mitigates the
  305. " issue of cache invalidation happening before a call to
  306. " s:update_untracked()
  307. call airline#extensions#branch#update_untracked_config(file, vcs)
  308. let s:vcs_config[vcs].untracked = {}
  309. endfor
  310. endfunction
  311. function! airline#extensions#branch#init(ext)
  312. call airline#parts#define_function('branch', 'airline#extensions#branch#get_head')
  313. autocmd ShellCmdPost,CmdwinLeave * unlet! b:airline_head b:airline_do_mq_check
  314. autocmd User AirlineBeforeRefresh unlet! b:airline_head b:airline_do_mq_check
  315. autocmd BufWritePost * call s:reset_untracked_cache(0)
  316. autocmd ShellCmdPost * call s:reset_untracked_cache(1)
  317. endfunction