summaryrefslogtreecommitdiffstats
path: root/autoload/vital/_crystal/Data
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2016-05-02 10:49:45 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2016-05-02 10:49:45 +0200
commitc200e7a0c587f70611b8dd702d0c3b378676a39a (patch)
tree960c7c88f634854cf3488d6d18ce42344875f8ef /autoload/vital/_crystal/Data
parent5529a5e8e21e4577e4cd3551f2cbad59b5b406e8 (diff)
downloadvim-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.vim446
-rw-r--r--autoload/vital/_crystal/Data/String.vim572
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