| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- " ___vital___
- " NOTE: lines between '" ___vital___' is generated by :Vitalize.
- " Do not mofidify the code nor insert new lines before '" ___vital___'
- if v:version > 703 || v:version == 703 && has('patch1170')
- function! vital#_neocomplete#Data#String#import() abort
- return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
- endfunction
- else
- function! s:_SID() abort
- return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
- endfunction
- execute join(['function! vital#_neocomplete#Data#String#import() abort', printf("return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
- delfunction s:_SID
- endif
- " ___vital___
- " Utilities for string.
- let s:save_cpo = &cpo
- set cpo&vim
- function! s:_vital_loaded(V) abort
- let s:V = a:V
- let s:L = s:V.import('Data.List')
- endfunction
- function! s:_vital_depends() abort
- return ['Data.List']
- endfunction
- function! s:_vital_created(module) abort
- " Expose script-local funcref
- if exists('s:strchars')
- let a:module.strchars = s:strchars
- endif
- if exists('s:wcswidth')
- let a:module.wcswidth = s:wcswidth
- endif
- 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')
- let s:strchars = function('strchars')
- 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
- let str = tr(a:str, "\t", ' ')
- let vcol = a:width + 2
- return matchstr(str, '.*\%<' . (vcol < 0 ? 0 : vcol) . 'v')
- endfunction
- function! s:strwidthpart_reverse(str, width) abort
- let str = tr(a:str, "\t", ' ')
- let vcol = s:wcswidth(str) - a:width
- return matchstr(str, '\%>' . (vcol < 0 ? 0 : vcol) . 'v.*')
- endfunction
- if v:version >= 703
- " Use builtin function.
- let s:wcswidth = function('strwidth')
- 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
- function! s:remove_ansi_sequences(text) abort
- return substitute(a:text, '\e\[\%(\%(\d\+;\)*\d\+\)\?[mK]', '', 'g')
- endfunction
- function! s:escape_pattern(str) abort
- " escape characters for no-magic
- return escape(a:str, '^$~.*[]\')
- endfunction
- function! s:unescape_pattern(str) abort
- " unescape characters for no-magic
- return s:unescape(a:str, '^$~.*[]\')
- endfunction
- function! s:unescape(str, chars) abort
- let chars = map(split(a:chars, '\zs'), 'escape(v:val, ''^$~.*[]\'')')
- return substitute(a:str, '\\\(' . join(chars, '\|') . '\)', '\1', 'g')
- endfunction
- function! s:iconv(expr, from, to) abort
- if a:from ==# '' || a:to ==# '' || a:from ==? a:to
- return a:expr
- endif
- let result = iconv(a:expr, a:from, a:to)
- return empty(result) ? a:expr : result
- endfunction
- " NOTE:
- " A definition of a TEXT file is "A file that contains characters organized
- " into one or more lines."
- " A definition of a LINE is "A sequence of zero or more non- <newline>s
- " plus a terminating <newline>"
- " That's why {stdin} always ends with <newline> ideally. However, there are
- " some programs which does not follow the POSIX rule and a Vim's way to join
- " List into TEXT; join({text}, "\n"); does not add <newline> to the end of
- " the last line.
- " That's why add a trailing <newline> if it does not exist.
- " REF:
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
- " :help split()
- " NOTE:
- " it does nothing if the text is a correct POSIX text
- function! s:repair_posix_text(text, ...) abort
- let newline = get(a:000, 0, "\n")
- return a:text =~# '\n$' ? a:text : a:text . newline
- endfunction
- " NOTE:
- " A definition of a TEXT file is "A file that contains characters organized
- " into one or more lines."
- " A definition of a LINE is "A sequence of zero or more non- <newline>s
- " plus a terminating <newline>"
- " REF:
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
- function! s:join_posix_lines(lines, ...) abort
- let newline = get(a:000, 0, "\n")
- return join(a:lines, newline) . newline
- endfunction
- " NOTE:
- " A definition of a TEXT file is "A file that contains characters organized
- " into one or more lines."
- " A definition of a LINE is "A sequence of zero or more non- <newline>s
- " plus a terminating <newline>"
- " TEXT into List; split({text}, '\r\?\n', 1); add an extra empty line at the
- " end of List because the end of TEXT ends with <newline> and keepempty=1 is
- " specified. (btw. keepempty=0 cannot be used because it will remove
- " emptylines in the head and the tail).
- " That's why removing a trailing <newline> before proceeding to 'split' is required
- " REF:
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
- " http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
- function! s:split_posix_text(text, ...) abort
- let newline = get(a:000, 0, '\r\?\n')
- let text = substitute(a:text, newline . '$', '', '')
- return split(text, newline, 1)
- endfunction
- let &cpo = s:save_cpo
- unlet s:save_cpo
- " vim:set et ts=2 sts=2 sw=2 tw=0:
|