diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2016-05-02 10:49:45 +0200 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2016-05-02 10:49:45 +0200 | 
| commit | c200e7a0c587f70611b8dd702d0c3b378676a39a (patch) | |
| tree | 960c7c88f634854cf3488d6d18ce42344875f8ef /autoload/vital/_crystal/Data | |
| parent | 5529a5e8e21e4577e4cd3551f2cbad59b5b406e8 (diff) | |
| download | vim-polyglot-c200e7a0c587f70611b8dd702d0c3b378676a39a.tar.gz vim-polyglot-c200e7a0c587f70611b8dd702d0c3b378676a39a.zip | |
Add crystal syntax, closes #118
Diffstat (limited to 'autoload/vital/_crystal/Data')
| -rw-r--r-- | autoload/vital/_crystal/Data/List.vim | 446 | ||||
| -rw-r--r-- | autoload/vital/_crystal/Data/String.vim | 572 | 
2 files changed, 1018 insertions, 0 deletions
| diff --git a/autoload/vital/_crystal/Data/List.vim b/autoload/vital/_crystal/Data/List.vim new file mode 100644 index 00000000..55703b3d --- /dev/null +++ b/autoload/vital/_crystal/Data/List.vim @@ -0,0 +1,446 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'crystal') == -1 +   +" Utilities for list. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:pop(list) abort +  return remove(a:list, -1) +endfunction + +function! s:push(list, val) abort +  call add(a:list, a:val) +  return a:list +endfunction + +function! s:shift(list) abort +  return remove(a:list, 0) +endfunction + +function! s:unshift(list, val) abort +  return insert(a:list, a:val) +endfunction + +function! s:cons(x, xs) abort +  return [a:x] + a:xs +endfunction + +function! s:conj(xs, x) abort +  return a:xs + [a:x] +endfunction + +" Removes duplicates from a list. +function! s:uniq(list) abort +  return s:uniq_by(a:list, 'v:val') +endfunction + +" Removes duplicates from a list. +function! s:uniq_by(list, f) abort +  let list = map(copy(a:list), printf('[v:val, %s]', a:f)) +  let i = 0 +  let seen = {} +  while i < len(list) +    let key = string(list[i][1]) +    if has_key(seen, key) +      call remove(list, i) +    else +      let seen[key] = 1 +      let i += 1 +    endif +  endwhile +  return map(list, 'v:val[0]') +endfunction + +function! s:clear(list) abort +  if !empty(a:list) +    unlet! a:list[0 : len(a:list) - 1] +  endif +  return a:list +endfunction + +" Concatenates a list of lists. +" XXX: Should we verify the input? +function! s:concat(list) abort +  let memo = [] +  for Value in a:list +    let memo += Value +  endfor +  return memo +endfunction + +" Take each elements from lists to a new list. +function! s:flatten(list, ...) abort +  let limit = a:0 > 0 ? a:1 : -1 +  let memo = [] +  if limit == 0 +    return a:list +  endif +  let limit -= 1 +  for Value in a:list +    let memo += +          \ type(Value) == type([]) ? +          \   s:flatten(Value, limit) : +          \   [Value] +    unlet! Value +  endfor +  return memo +endfunction + +" Sorts a list with expression to compare each two values. +" a:a and a:b can be used in {expr}. +function! s:sort(list, expr) abort +  if type(a:expr) == type(function('function')) +    return sort(a:list, a:expr) +  endif +  let s:expr = a:expr +  return sort(a:list, 's:_compare') +endfunction + +function! s:_compare(a, b) abort +  return eval(s:expr) +endfunction + +" Sorts a list using a set of keys generated by mapping the values in the list +" through the given expr. +" v:val is used in {expr} +function! s:sort_by(list, expr) abort +  let pairs = map(a:list, printf('[v:val, %s]', a:expr)) +  return map(s:sort(pairs, +  \      'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]') +endfunction + +" Returns a maximum value in {list} through given {expr}. +" Returns 0 if {list} is empty. +" v:val is used in {expr} +function! s:max_by(list, expr) abort +  if empty(a:list) +    return 0 +  endif +  let list = map(copy(a:list), a:expr) +  return a:list[index(list, max(list))] +endfunction + +" Returns a minimum value in {list} through given {expr}. +" Returns 0 if {list} is empty. +" v:val is used in {expr} +" FIXME: -0x80000000 == 0x80000000 +function! s:min_by(list, expr) abort +  return s:max_by(a:list, '-(' . a:expr . ')') +endfunction + +" Returns List of character sequence between [a:from, a:to] +" e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c'] +function! s:char_range(from, to) abort +  return map( +  \   range(char2nr(a:from), char2nr(a:to)), +  \   'nr2char(v:val)' +  \) +endfunction + +" Returns true if a:list has a:value. +" Returns false otherwise. +function! s:has(list, value) abort +  return index(a:list, a:value) isnot -1 +endfunction + +" Returns true if a:list[a:index] exists. +" Returns false otherwise. +" NOTE: Returns false when a:index is negative number. +function! s:has_index(list, index) abort +  " Return true when negative index? +  " let index = a:index >= 0 ? a:index : len(a:list) + a:index +  return 0 <= a:index && a:index < len(a:list) +endfunction + +" similar to Haskell's Data.List.span +function! s:span(f, xs) abort +  let border = len(a:xs) +  for i in range(len(a:xs)) +    if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) +      let border = i +      break +    endif +  endfor +  return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]] +endfunction + +" similar to Haskell's Data.List.break +function! s:break(f, xs) abort +  return s:span(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Data.List.takeWhile +function! s:take_while(f, xs) abort +  return s:span(a:f, a:xs)[0] +endfunction + +" similar to Haskell's Data.List.partition +function! s:partition(f, xs) abort +  return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')] +endfunction + +" similar to Haskell's Prelude.all +function! s:all(f, xs) abort +  return !s:any(printf('!(%s)', a:f), a:xs) +endfunction + +" similar to Haskell's Prelude.any +function! s:any(f, xs) abort +  return !empty(filter(map(copy(a:xs), a:f), 'v:val')) +endfunction + +" similar to Haskell's Prelude.and +function! s:and(xs) abort +  return s:all('v:val', a:xs) +endfunction + +" similar to Haskell's Prelude.or +function! s:or(xs) abort +  return s:any('v:val', a:xs) +endfunction + +function! s:map_accum(expr, xs, init) abort +  let memo = [] +  let init = a:init +  for x in a:xs +    let expr = substitute(a:expr, 'v:memo', init, 'g') +    let expr = substitute(expr, 'v:val', x, 'g') +    let [tmp, init] = eval(expr) +    call add(memo, tmp) +  endfor +  return memo +endfunction + +" similar to Haskell's Prelude.foldl +function! s:foldl(f, init, xs) abort +  let memo = a:init +  for x in a:xs +    let expr = substitute(a:f, 'v:val', string(x), 'g') +    let expr = substitute(expr, 'v:memo', string(memo), 'g') +    unlet memo +    let memo = eval(expr) +  endfor +  return memo +endfunction + +" similar to Haskell's Prelude.foldl1 +function! s:foldl1(f, xs) abort +  if len(a:xs) == 0 +    throw 'vital: Data.List: foldl1' +  endif +  return s:foldl(a:f, a:xs[0], a:xs[1:]) +endfunction + +" similar to Haskell's Prelude.foldr +function! s:foldr(f, init, xs) abort +  return s:foldl(a:f, a:init, reverse(copy(a:xs))) +endfunction + +" similar to Haskell's Prelude.fold11 +function! s:foldr1(f, xs) abort +  if len(a:xs) == 0 +    throw 'vital: Data.List: foldr1' +  endif +  return s:foldr(a:f, a:xs[-1], a:xs[0:-2]) +endfunction + +" similar to python's zip() +function! s:zip(...) abort +  return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')") +endfunction + +" similar to zip(), but goes until the longer one. +function! s:zip_fill(xs, ys, filler) abort +  if empty(a:xs) && empty(a:ys) +    return [] +  elseif empty(a:ys) +    return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler)) +  elseif empty(a:xs) +    return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler)) +  else +    return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler)) +  endif +endfunction + +" Inspired by Ruby's with_index method. +function! s:with_index(list, ...) abort +  let base = a:0 > 0 ? a:1 : 0 +  return map(copy(a:list), '[v:val, v:key + base]') +endfunction + +" similar to Ruby's detect or Haskell's find. +function! s:find(list, default, f) abort +  for x in a:list +    if eval(substitute(a:f, 'v:val', string(x), 'g')) +      return x +    endif +  endfor +  return a:default +endfunction + +" Returns the index of the first element which satisfies the given expr. +function! s:find_index(xs, f, ...) abort +  let len = len(a:xs) +  let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 +  let default = a:0 > 1 ? a:2 : -1 +  if start >=# len || start < 0 +    return default +  endif +  for i in range(start, len - 1) +    if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) +      return i +    endif +  endfor +  return default +endfunction + +" Returns the index of the last element which satisfies the given expr. +function! s:find_last_index(xs, f, ...) abort +  let len = len(a:xs) +  let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1 +  let default = a:0 > 1 ? a:2 : -1 +  if start >=# len || start < 0 +    return default +  endif +  for i in range(start, 0, -1) +    if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) +      return i +    endif +  endfor +  return default +endfunction + +" Similar to find_index but returns the list of indices satisfying the given expr. +function! s:find_indices(xs, f, ...) abort +  let len = len(a:xs) +  let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 +  let result = [] +  if start >=# len || start < 0 +    return result +  endif +  for i in range(start, len - 1) +    if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) +      call add(result, i) +    endif +  endfor +  return result +endfunction + +" Return non-zero if a:list1 and a:list2 have any common item(s). +" Return zero otherwise. +function! s:has_common_items(list1, list2) abort +  return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1')) +endfunction + +function! s:intersect(list1, list2) abort +  let items = [] +  " for funcref +  for X in a:list1 +    if index(a:list2, X) != -1 && index(items, X) == -1 +      let items += [X] +    endif +  endfor +  return items +endfunction + +" similar to Ruby's group_by. +function! s:group_by(xs, f) abort +  let result = {} +  let list = map(copy(a:xs), printf('[v:val, %s]', a:f)) +  for x in list +    let Val = x[0] +    let key = type(x[1]) !=# type('') ? string(x[1]) : x[1] +    if has_key(result, key) +      call add(result[key], Val) +    else +      let result[key] = [Val] +    endif +    unlet Val +  endfor +  return result +endfunction + +function! s:_default_compare(a, b) abort +  return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0 +endfunction + +function! s:binary_search(list, value, ...) abort +  let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare' +  let dic = a:0 >= 2 ? a:2 : {} +  let start = 0 +  let end = len(a:list) - 1 + +  while 1 +    if start > end +      return -1 +    endif + +    let middle = (start + end) / 2 + +    let compared = call(Predicate, [a:value, a:list[middle]], dic) + +    if compared < 0 +      let end = middle - 1 +    elseif compared > 0 +      let start = middle + 1 +    else +      return middle +    endif +  endwhile +endfunction + +function! s:product(lists) abort +  let result = [[]] +  for pool in a:lists +    let tmp = [] +    for x in result +      let tmp += map(copy(pool), 'x + [v:val]') +    endfor +    let result = tmp +  endfor +  return result +endfunction + +function! s:permutations(list, ...) abort +  if a:0 > 1 +    throw 'vital: Data.List: too many arguments' +  endif +  let r = a:0 == 1 ? a:1 : len(a:list) +  if r > len(a:list) +    return [] +  elseif r < 0 +    throw 'vital: Data.List: {r} must be non-negative integer' +  endif +  let n = len(a:list) +  let result = [] +  for indices in s:product(map(range(r), 'range(n)')) +    if len(s:uniq(indices)) == r +      call add(result, map(indices, 'a:list[v:val]')) +    endif +  endfor +  return result +endfunction + +function! s:combinations(list, r) abort +  if a:r > len(a:list) +    return [] +  elseif a:r < 0 +    throw 'vital: Data:List: {r} must be non-negative integer' +  endif +  let n = len(a:list) +  let result = [] +  for indices in s:permutations(range(n), a:r) +    if s:sort(copy(indices), 'a:a - a:b') == indices +      call add(result, map(indices, 'a:list[v:val]')) +    endif +  endfor +  return result +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et ts=2 sts=2 sw=2 tw=0: + +endif diff --git a/autoload/vital/_crystal/Data/String.vim b/autoload/vital/_crystal/Data/String.vim new file mode 100644 index 00000000..c2e2382c --- /dev/null +++ b/autoload/vital/_crystal/Data/String.vim @@ -0,0 +1,572 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'crystal') == -1 +   +" Utilities for string. + +let s:save_cpo = &cpo +set cpo&vim + +function! s:_vital_loaded(V) abort +  let s:V = a:V +  let s:P = s:V.import('Prelude') +  let s:L = s:V.import('Data.List') +endfunction + +function! s:_vital_depends() abort +  return ['Prelude', 'Data.List'] +endfunction + +" Substitute a:from => a:to by string. +" To substitute by pattern, use substitute() instead. +function! s:replace(str, from, to) abort +  return s:_replace(a:str, a:from, a:to, 'g') +endfunction + +" Substitute a:from => a:to only once. +" cf. s:replace() +function! s:replace_first(str, from, to) abort +  return s:_replace(a:str, a:from, a:to, '') +endfunction + +" implement of replace() and replace_first() +function! s:_replace(str, from, to, flags) abort +  return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags) +endfunction + +function! s:scan(str, pattern) abort +  let list = [] +  call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g') +  return list +endfunction + +function! s:reverse(str) abort +  return join(reverse(split(a:str, '.\zs')), '') +endfunction + +function! s:starts_with(str, prefix) abort +  return stridx(a:str, a:prefix) == 0 +endfunction + +function! s:ends_with(str, suffix) abort +  let idx = strridx(a:str, a:suffix) +  return 0 <= idx && idx + len(a:suffix) == len(a:str) +endfunction + +function! s:common_head(strs) abort +  if empty(a:strs) +    return '' +  endif +  let len = len(a:strs) +  if len == 1 +    return a:strs[0] +  endif +  let strs = len == 2 ? a:strs : sort(copy(a:strs)) +  let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g') +  return pat == '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']') +endfunction + +" Split to two elements of List. ([left, right]) +" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache'] +function! s:split_leftright(expr, pattern) abort +  let [left, _, right] = s:split3(a:expr, a:pattern) +  return [left, right] +endfunction + +function! s:split3(expr, pattern) abort +  let ERROR = ['', '', ''] +  if a:expr ==# '' || a:pattern ==# '' +    return ERROR +  endif +  let begin = match(a:expr, a:pattern) +  if begin is -1 +    return ERROR +  endif +  let end   = matchend(a:expr, a:pattern) +  let left  = begin <=# 0 ? '' : a:expr[: begin - 1] +  let right = a:expr[end :] +  return [left, a:expr[begin : end-1], right] +endfunction + +" Slices into strings determines the number of substrings. +" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache'] +function! s:nsplit(expr, n, ...) abort +  let pattern = get(a:000, 0, '\s') +  let keepempty = get(a:000, 1, 1) +  let ret = [] +  let expr = a:expr +  if a:n <= 1 +    return [expr] +  endif +  while 1 +    let pos = match(expr, pattern) +    if pos == -1 +      if expr !~ pattern || keepempty +        call add(ret, expr) +      endif +      break +    elseif pos >= 0 +      let left = pos > 0 ? expr[:pos-1] : '' +      if pos > 0 || keepempty +        call add(ret, left) +      endif +      let ml = len(matchstr(expr, pattern)) +      if pos == 0 && ml == 0 +        let pos = 1 +      endif +      let expr = expr[pos+ml :] +    endif +    if len(expr) == 0 +      break +    endif +    if len(ret) == a:n - 1 +      call add(ret, expr) +      break +    endif +  endwhile +  return ret +endfunction + +" Returns the number of character in a:str. +" NOTE: This returns proper value +" even if a:str contains multibyte character(s). +" s:strchars(str) {{{ +if exists('*strchars') +  function! s:strchars(str) abort +    return strchars(a:str) +  endfunction +else +  function! s:strchars(str) abort +    return strlen(substitute(copy(a:str), '.', 'x', 'g')) +  endfunction +endif "}}} + +" Returns the bool of contains any multibyte character in s:str +function! s:contains_multibyte(str) abort "{{{ +  return strlen(a:str) != s:strchars(a:str) +endfunction "}}} + +" Remove last character from a:str. +" NOTE: This returns proper value +" even if a:str contains multibyte character(s). +function! s:chop(str) abort "{{{ +  return substitute(a:str, '.$', '', '') +endfunction "}}} + +" Remove last \r,\n,\r\n from a:str. +function! s:chomp(str) abort "{{{ +  return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '') +endfunction "}}} + +" wrap() and its internal functions +" * _split_by_wcswidth_once() +" * _split_by_wcswidth() +" * _concat() +" * wrap() +" +" NOTE _concat() is just a copy of Data.List.concat(). +" FIXME don't repeat yourself +function! s:_split_by_wcswidth_once(body, x) abort +  let fst = s:strwidthpart(a:body, a:x) +  let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst)) +  return [fst, snd] +endfunction + +function! s:_split_by_wcswidth(body, x) abort +  let memo = [] +  let body = a:body +  while s:wcswidth(body) > a:x +    let [tmp, body] = s:_split_by_wcswidth_once(body, a:x) +    call add(memo, tmp) +  endwhile +  call add(memo, body) +  return memo +endfunction + +function! s:trim(str) abort +  return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') +endfunction + +function! s:trim_start(str) abort +  return matchstr(a:str,'^\s*\zs.\{-}$') +endfunction + +function! s:trim_end(str) abort +  return matchstr(a:str,'^.\{-}\ze\s*$') +endfunction + +function! s:wrap(str,...) abort +  let _columns = a:0 > 0 ? a:1 : &columns +  return s:L.concat( +        \ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)')) +endfunction + +function! s:nr2byte(nr) abort +  if a:nr < 0x80 +    return nr2char(a:nr) +  elseif a:nr < 0x800 +    return nr2char(a:nr/64+192).nr2char(a:nr%64+128) +  else +    return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128) +  endif +endfunction + +function! s:nr2enc_char(charcode) abort +  if &encoding == 'utf-8' +    return nr2char(a:charcode) +  endif +  let char = s:nr2byte(a:charcode) +  if strlen(char) > 1 +    let char = strtrans(iconv(char, 'utf-8', &encoding)) +  endif +  return char +endfunction + +function! s:nr2hex(nr) abort +  let n = a:nr +  let r = "" +  while n +    let r = '0123456789ABCDEF'[n % 16] . r +    let n = n / 16 +  endwhile +  return r +endfunction + +" If a ==# b, returns -1. +" If a !=# b, returns first index of different character. +function! s:diffidx(a, b) abort +  return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b])) +endfunction + +function! s:substitute_last(expr, pat, sub) abort +  return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '') +endfunction + +function! s:dstring(expr) abort +  let x = substitute(string(a:expr), "^'\\|'$", '', 'g') +  let x = substitute(x, "''", "'", 'g') +  return printf('"%s"', escape(x, '"')) +endfunction + +function! s:lines(str) abort +  return split(a:str, '\r\?\n') +endfunction + +function! s:_pad_with_char(str, left, right, char) abort +  return repeat(a:char, a:left). a:str. repeat(a:char, a:right) +endfunction + +function! s:pad_left(str, width, ...) abort +  let char = get(a:, 1, ' ') +  if strdisplaywidth(char) != 1 +    throw "vital: Data.String: Can't use non-half-width characters for padding." +  endif +  let left = max([0, a:width - strdisplaywidth(a:str)]) +  return s:_pad_with_char(a:str, left, 0, char) +endfunction + +function! s:pad_right(str, width, ...) abort +  let char = get(a:, 1, ' ') +  if strdisplaywidth(char) != 1 +    throw "vital: Data.String: Can't use non-half-width characters for padding." +  endif +  let right = max([0, a:width - strdisplaywidth(a:str)]) +  return s:_pad_with_char(a:str, 0, right, char) +endfunction + +function! s:pad_both_sides(str, width, ...) abort +  let char = get(a:, 1, ' ') +  if strdisplaywidth(char) != 1 +    throw "vital: Data.String: Can't use non-half-width characters for padding." +  endif +  let space = max([0, a:width - strdisplaywidth(a:str)]) +  let left = space / 2 +  let right = space - left +  return s:_pad_with_char(a:str, left, right, char) +endfunction + +function! s:pad_between_letters(str, width, ...) abort +  let char = get(a:, 1, ' ') +  if strdisplaywidth(char) != 1 +    throw "vital: Data.String: Can't use non-half-width characters for padding." +  endif +  let letters = split(a:str, '\zs') +  let each_width = a:width / len(letters) +  let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '') +  if a:width - strdisplaywidth(str) > 0 +    return char. s:pad_both_sides(str, a:width - 1, char) +  endif +  return str +endfunction + +function! s:justify_equal_spacing(str, width, ...) abort +  let char = get(a:, 1, ' ') +  if strdisplaywidth(char) != 1 +    throw "vital: Data.String: Can't use non-half-width characters for padding." +  endif +  let letters = split(a:str, '\zs') +  let first_letter = letters[0] +  " {width w/o the first letter} / {length w/o the first letter} +  let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1) +  let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1) +  return first_letter. join(s:L.concat([ +\     map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'), +\     map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)') +\   ]), '') +endfunction + +function! s:levenshtein_distance(str1, str2) abort +  let letters1 = split(a:str1, '\zs') +  let letters2 = split(a:str2, '\zs') +  let length1 = len(letters1) +  let length2 = len(letters2) +  let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), "0")') + +  for i1 in range(0, length1) +    let distances[i1][0] = i1 +  endfor +  for i2 in range(0, length2) +    let distances[0][i2] = i2 +  endfor + +  for i1 in range(1, length1) +    for i2 in range(1, length2) +      let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1 + +      let distances[i1][i2] = min([ +      \ distances[i1 - 1][i2    ] + 1, +      \ distances[i1    ][i2 - 1] + 1, +      \ distances[i1 - 1][i2 - 1] + cost, +      \]) +    endfor +  endfor + +  return distances[length1][length2] +endfunction + +function! s:padding_by_displaywidth(expr, width, float) abort +  let padding_char = ' ' +  let n = a:width - strdisplaywidth(a:expr) +  if n <= 0 +    let n = 0 +  endif +  if a:float < 0 +    return a:expr . repeat(padding_char, n) +  elseif 0 < a:float +    return repeat(padding_char, n) . a:expr +  else +    if n % 2 is 0 +      return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2) +    else +      return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char +    endif +  endif +endfunction + +function! s:split_by_displaywidth(expr, width, float, is_wrap) abort +  if a:width is 0 +    return [''] +  endif + +  let lines = [] + +  let cs = split(a:expr, '\zs') +  let cs_index = 0 + +  let text = '' +  while cs_index < len(cs) +    if cs[cs_index] is "\n" +      let text = s:padding_by_displaywidth(text, a:width, a:float) +      let lines += [text] +      let text = '' +    else +      let w = strdisplaywidth(text . cs[cs_index]) + +      if w < a:width +        let text .= cs[cs_index] +      elseif a:width < w +        let text = s:padding_by_displaywidth(text, a:width, a:float) +      else +        let text .= cs[cs_index] +      endif + +      if a:width <= w +        let lines += [text] +        let text = '' +        if a:is_wrap +          if a:width < w +            if a:width < strdisplaywidth(cs[cs_index]) +              while get(cs, cs_index, "\n") isnot "\n" +                let cs_index += 1 +              endwhile +              continue +            else +              let text = cs[cs_index] +            endif +          endif +        else +          while get(cs, cs_index, "\n") isnot "\n" +            let cs_index += 1 +          endwhile +          continue +        endif +      endif + +    endif +    let cs_index += 1 +  endwhile + +  if !empty(text) +    let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ] +  endif + +  return lines +endfunction + +function! s:hash(str) abort +  if exists('*sha256') +    return sha256(a:str) +  else +    " This gives up sha256ing but just adds up char with index. +    let sum = 0 +    for i in range(len(a:str)) +      let sum += char2nr(a:str[i]) * (i + 1) +    endfor + +    return printf('%x', sum) +  endif +endfunction + +function! s:truncate(str, width) abort +  " Original function is from mattn. +  " http://github.com/mattn/googlereader-vim/tree/master + +  if a:str =~# '^[\x00-\x7f]*$' +    return len(a:str) < a:width ? +          \ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width) +  endif + +  let ret = a:str +  let width = s:wcswidth(a:str) +  if width > a:width +    let ret = s:strwidthpart(ret, a:width) +    let width = s:wcswidth(ret) +  endif + +  if width < a:width +    let ret .= repeat(' ', a:width - width) +  endif + +  return ret +endfunction + +function! s:truncate_skipping(str, max, footer_width, separator) abort +  let width = s:wcswidth(a:str) +  if width <= a:max +    let ret = a:str +  else +    let header_width = a:max - s:wcswidth(a:separator) - a:footer_width +    let ret = s:strwidthpart(a:str, header_width) . a:separator +          \ . s:strwidthpart_reverse(a:str, a:footer_width) +  endif +  return s:truncate(ret, a:max) +endfunction + +function! s:strwidthpart(str, width) abort +  if a:width <= 0 +    return '' +  endif +  let strarr = split(a:str, '\zs') +  let width = s:wcswidth(a:str) +  let index = len(strarr) +  let diff = (index + 1) / 2 +  let rightindex = index - 1 +  while width > a:width +    let index = max([rightindex - diff + 1, 0]) +    let partwidth = s:wcswidth(join(strarr[(index):(rightindex)], '')) +    if width - partwidth >= a:width || diff <= 1 +      let width -= partwidth +      let rightindex = index - 1 +    endif +    if diff > 1 +      let diff = diff / 2 +    endif +  endwhile +  return index ? join(strarr[:index - 1], '') : '' +endfunction + +function! s:strwidthpart_reverse(str, width) abort +  if a:width <= 0 +    return '' +  endif +  let strarr = split(a:str, '\zs') +  let width = s:wcswidth(a:str) +  let strlen = len(strarr) +  let diff = (strlen + 1) / 2 +  let leftindex = 0 +  let index = -1 +  while width > a:width +    let index = min([leftindex + diff, strlen]) - 1 +    let partwidth = s:wcswidth(join(strarr[(leftindex):(index)], '')) +    if width - partwidth >= a:width || diff <= 1 +      let width -= partwidth +      let leftindex = index + 1 +    endif +    if diff > 1 +      let diff = diff / 2 +    endif +  endwhile +  return index < strlen ? join(strarr[(index + 1):], '') : '' +endfunction + +if v:version >= 703 +  " Use builtin function. +  function! s:wcswidth(str) abort +    return strwidth(a:str) +  endfunction +else +  function! s:wcswidth(str) abort +    if a:str =~# '^[\x00-\x7f]*$' +      return strlen(a:str) +    endif +    let mx_first = '^\(.\)' +    let str = a:str +    let width = 0 +    while 1 +      let ucs = char2nr(substitute(str, mx_first, '\1', '')) +      if ucs == 0 +        break +      endif +      let width += s:_wcwidth(ucs) +      let str = substitute(str, mx_first, '', '') +    endwhile +    return width +  endfunction + +  " UTF-8 only. +  function! s:_wcwidth(ucs) abort +    let ucs = a:ucs +    if (ucs >= 0x1100 +          \  && (ucs <= 0x115f +          \  || ucs == 0x2329 +          \  || ucs == 0x232a +          \  || (ucs >= 0x2e80 && ucs <= 0xa4cf +          \      && ucs != 0x303f) +          \  || (ucs >= 0xac00 && ucs <= 0xd7a3) +          \  || (ucs >= 0xf900 && ucs <= 0xfaff) +          \  || (ucs >= 0xfe30 && ucs <= 0xfe6f) +          \  || (ucs >= 0xff00 && ucs <= 0xff60) +          \  || (ucs >= 0xffe0 && ucs <= 0xffe6) +          \  || (ucs >= 0x20000 && ucs <= 0x2fffd) +          \  || (ucs >= 0x30000 && ucs <= 0x3fffd) +          \  )) +      return 2 +    endif +    return 1 +  endfunction +endif + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et ts=2 sts=2 sw=2 tw=0: + +endif | 
