Skip to content

Instantly share code, notes, and snippets.

Created November 18, 2017 17:00
Show Gist options
  • Save anonymous/fa35a5128886c0ecbca8f7c14fbcdde1 to your computer and use it in GitHub Desktop.
Save anonymous/fa35a5128886c0ecbca8f7c14fbcdde1 to your computer and use it in GitHub Desktop.
stdin
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