let s:String = vital#gina#import('Data.String') let s:Formatter = vital#gina#import('Data.String.Formatter') let s:N_COLORS = 16 let s:FORMAT_MAP = { \ 'su': 'summary', \ 'au': 'author', \ 'ma': 'mark', \ 'in': 'index', \ 'ti': 'timestamp', \} function! gina#command#blame#formatter#new(width, current, revisions, ...) abort let options = extend({ \ 'format': v:null, \ 'separator': v:null, \ 'current_mark': v:null, \ 'timestamp_months': v:null, \ 'timestamp_format1': v:null, \ 'timestamp_format2': v:null, \}, get(a:000, 0, {}) \) let formatter = deepcopy(s:formatter) let formatter._cache = {} let formatter._width = a:width let formatter._current = empty(a:current) ? 'X' : a:current let formatter._revisions = s:index_revisions(a:revisions) let formatter._previous = 1 let formatter._timestamper = gina#core#timestamper#new({ \ 'months': empty(options.timestamp_months) \ ? g:gina#command#blame#formatter#timestamp_months \ : options.timestamp_months, \ 'format1': empty(options.timestamp_format1) \ ? g:gina#command#blame#formatter#timestamp_format1 \ : options.timestamp_format1, \ 'format2': empty(options.timestamp_format2) \ ? g:gina#command#blame#formatter#timestamp_format2 \ : options.timestamp_format2, \}) let formatter._current_mark = empty(options.current_mark) \ ? g:gina#command#blame#formatter#current_mark \ : options.current_mark let formatter._separator = empty(options.separator) \ ? g:gina#command#blame#formatter#separator \ : options.separator let formatter._format = empty(options.format) \ ? g:gina#command#blame#formatter#format \ : options.format return formatter endfunction " Private -------------------------------------------------------------------- function! s:index_revisions(revisions) abort let n = s:calc_nindicators(a:revisions) let revisions = deepcopy(a:revisions) let keys = keys(revisions) for index in range(len(revisions)) let revisions[keys[index]].index = s:String.pad_left( \ s:String.nr2hex(index), n, '0' \) endfor return revisions endfunction function! s:calc_nindicators(revisions) abort let n = len(a:revisions) let x = 1 while pow(s:N_COLORS, x) < n let x+= 1 endwhile return x endfunction " Formatter ------------------------------------------------------------------ let s:formatter = {} function! s:formatter.format(chunk) abort let revision = a:chunk.revision let revinfo = self._revisions[revision] let content = repeat( \ [self._format_line(a:chunk, revision, revinfo)], \ a:chunk.nlines, \) " Fill missing lines from previous let mlines = a:chunk.lnum - self._previous let self._previous = a:chunk.lnum + a:chunk.nlines return repeat([''], mlines) + content endfunction function! s:formatter._format_line(chunk, revision, revinfo) abort if has_key(self._cache, a:revision) return self._cache[a:revision] endif let precursor = s:Formatter.format(self._format, s:FORMAT_MAP, { \ 'summary': a:revinfo.summary, \ 'author': a:revinfo.author, \ 'index': a:revinfo.index, \ 'mark': a:revision =~# '^' . self._current \ ? self._current_mark \ : repeat(' ', len(self._current_mark)), \ 'timestamp': self._timestamper.format( \ a:revinfo.author_time, \ a:revinfo.author_tz \ ), \}) let [head, tail] = split(precursor . '%=', '%=', 1)[0 : 1] let width = self._width - strwidth(tail) - 1 let head = s:String.truncate_skipping(head, width, 3, self._separator) let head = s:String.pad_right(head, width) let self._cache[a:revision] = head . ' ' . tail return self._cache[a:revision] endfunction " Config --------------------------------------------------------------------- call gina#config(expand(''), { \ 'format': '%su%=on %ti %ma%in', \ 'separator': '...', \ 'current_mark': '|', \ 'timestamp_months': 3, \ 'timestamp_format1': '%d %b', \ 'timestamp_format2': '%d %b, %Y', \})