Created
November 18, 2017 17:00
-
-
Save anonymous/fa35a5128886c0ecbca8f7c14fbcdde1 to your computer and use it in GitHub Desktop.
stdin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if exists('g:loaded_selection_autoload') | |
finish | |
endif | |
let g:loaded_selection_autoload = 1 | |
"" | |
" Compare two marks | |
" | |
" Function is compatible with sort(). | |
" | |
" @param[in] a First mark, expects getpos() output. | |
" @param[in] b Second mark, expects getpos() output. | |
" | |
" @return -1, 0 or 1 for a:a < a:b, a:a == a:b and a:a > a:b respectively. May | |
" throw `selection:invcmp:` exception in case bufnum (first item in the | |
" marks list) differences in supplied marks. | |
function selection#compare_marks(a, b) abort | |
if a:a[0] != a:b[0] | |
throw 'selection:invcmp:Can only compare marks with identical bufnum' | |
endif | |
return (a:a[1] == a:b[1] | |
\ ? (a:a[2] == a:b[2] | |
\ ? (a:a[3] == a:b[3] | |
\ ? 0 | |
\ : 1 - 2 * (a:a[3] < a:b[3])) | |
\ : 1 - 2 * (a:a[2] < a:b[2])) | |
\ : 1 - 2 * (a:a[1] < a:b[1])) | |
endfunction | |
"" | |
" Delete given range of lines | |
" | |
" @param[in] start First line to delete. | |
" @param[in] end Last line to delete. | |
function s:del_lines(start, end) abort | |
execute a:start . ',' . a:end 'delete' '_' | |
endfunction | |
"" | |
" Given virtual column, find real column | |
" | |
" @param[in] lnr Number of line for which to find column. | |
" @param[in] vcol Virtual column for which to find column. | |
function s:find_col(lnr, vcol) abort | |
let lscol = a:vcol | |
let lsvcol = virtcol([a:lnr, lscol]) | |
if lsvcol != a:vcol | |
if lsvcol > a:vcol | |
while lsvcol > a:vcol && lscol > 1 | |
let lscol -= 1 | |
let lsvcol = virtcol([a:lnr, lscol]) | |
endwhile | |
else | |
while lsvcol < a:vcol && lsvcol > 1 | |
let lscol += 1 | |
let lsvcol = virtcol([a:lnr, lscol]) | |
endwhile | |
endif | |
endif | |
return lscol | |
endfunction | |
"" | |
" Modify or get range between given marks | |
" | |
" @param[in] type Type of the range: | |
" | |
" - "c", "char" or "v" for characterwise range. | |
" - "l", "line" or "V" for linewise range. | |
" - "b", "block" or "\<C-v>" for blockwise range. Latter can | |
" be followed by number. | |
" @param[in] width For blockwise range: range width. If it is zero, then width | |
" specified after "\<C-v>" in type is used. If no width is | |
" specified there as well then width is determined using | |
" start and end marks. | |
" @param[in] start Start of the range, format of the mark is identical to | |
" getpos() return. Out of these four values only second and | |
" third are used, fourth may be used in case of blockwise | |
" selection. | |
" @param[in] end Like start argument, but end of the range. | |
" @param[in] replacement What to replace range with, readfile()-style. May be | |
" zero, in this case nothing is replaced. To delete, | |
" supply an empty list. In case of blockwise ranges one | |
" must supply either an empty list or list containing | |
" a number of lines equal to number of lines in the | |
" range. | |
" | |
" @return Contents of the range, readfile()-style. May throw `selection:invcmp:` | |
" in case start and end marks contain different bufnum (first list item) | |
" component. May throw `selection:invbuf:` when trying to operate with | |
" positions not in current buffer. | |
function selection#modrange(type, width, start, end, replacement) abort | |
let [start, end] = sort([a:start, a:end], function('selection#compare_marks')) | |
let buf = (start[0] == 0 ? bufnr('%') : start[0]) | |
if buf != bufnr('%') && replacement isnot 0 | |
" Missing append() | |
throw 'selection:invbuf:Can only operate on current buffer' | |
endif | |
if a:type[0] is# 'l' || a:type[0] is# 'V' | |
let ret = getbufline(buf, start[1], end[1]) | |
if a:replacement isnot 0 | |
call s:del_lines(start[1], end[1]) | |
call append(start[1] - 1, a:replacement) | |
endif | |
elseif a:type[0] is# 'c' || a:type[0] is# 'v' | |
let eecol = col([end[1], '$']) | |
let slinestr = getbufline(buf, start[1])[0] | |
if end[2] >= eecol && a:replacement isnot 0 | |
let replacement = a:replacement[:] | |
if empty(replacement) | |
call add(replacement, getbufline(buf, end[1] + 1)[0]) | |
else | |
let replacement[-1] = replacement[-1] . getbufline(buf, end[1] + 1)[0] | |
endif | |
call s:del_lines(end[1] + 1, end[1] + 1) | |
endif | |
if start[1] == end[1] | |
let ret = [slinestr[start[2] - 1 : slinestr[end[2] - 1]]] | |
if a:replacement isnot 0 | |
if len(replacement) <= 1 | |
call setbufline(buf, start[1], | |
\ slinestr[: start[2] - 2] | |
\ . get(replacement, 0, '') | |
\ . slinestr[end[2] :]) | |
else | |
call setbufline(buf, start[1], | |
\ slinestr[: start[2] - 2] | |
\ . get(replacement, 0, '')) | |
call append(start[1], | |
\ replacement[1 : -2] | |
\ + [replacement[-1] . slinestr[end[2]:]]) | |
endif | |
endif | |
else | |
let ret = ([getbufline(buf, start[1])[0][start[2] - 1 :]] | |
\ + getbufline(buf, start[1] + 1, end[1] - 1) | |
\ + [getbufline(buf, end[1])[0][: end[2] - 1]]) | |
if a:replacement isnot 0 | |
if len(replacement) > 1 | |
call setbufline(buf, start[1], | |
\ slinestr[: start[2] - 2] . replacement[0]) | |
call setbufline(buf, end[1], | |
\ replacement[-1] | |
\ . getbufline(buf, end[1])[0][end[2] :]) | |
if start[1] > end[1] + 1 | |
call s:del_lines(start[1] + 1, end[1] - 1) | |
endif | |
call append(start[1], replacement[1 : -2]) | |
else | |
call setbufline(buf, start[1], | |
\ slinestr[: start[2] - 2] | |
\ . get(replacement, 0, '') | |
\ . getbufline(buf, end[1])[0][end[2] :]) | |
call s:del_lines(start[1] + 1, end[1]) | |
endif | |
endif | |
endif | |
if end[2] >= eecol | |
call add(ret, '') | |
endif | |
else | |
if buf != bufnr('%') | |
" Missing virtcol() | |
throw 'selection:invbuf:Can only operate on current buffer' | |
endif | |
let ret = [] | |
let svcol = virtcol(start[1:2]) + start[3] | |
let evcol = virtcol(end[1:2]) + end[3] | |
let [svcol, evcol] = sort([svcol, evcol], 'n') | |
for lnr in range(start[1], end[1]) | |
let idx = lnr - start[1] | |
let linestr = getbufline(buf, lnr)[0] | |
let levcol = virtcol([lnr, '$']) | |
if svcol >= levcol | |
call add(ret, '') | |
if !empty(a:replacement) | |
call setbufline(buf, lnr, | |
\ linestr | |
\ . repeat(' ', svcol - levcol) | |
\ . get(a:replacement, idx, '')) | |
endif | |
continue | |
endif | |
let lscol = s:find_col(lnr, svcol) | |
if evcol >= levcol | |
call add(ret, linestr[lscol - 1 :]) | |
if a:replacement isnot 0 | |
call setbufline(buf, lnr, | |
\ linestr[: lscol - 2] . get(a:replacement, idx, '')) | |
endif | |
continue | |
endif | |
let lecol = s:find_col(lnr, evcol) | |
call add(ret, linestr[lscol - 1 : lecol - 1]) | |
if a:replacement isnot 0 | |
call setbufline(buf, lnr, | |
\ linestr[: lscol - 2] | |
\ . get(a:replacement, idx, '') | |
\ . linestr[lecol :]) | |
endif | |
endfor | |
endif | |
return ret | |
endfunction |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment