I'd like to define a text object for tab-delimited fields in a tsv file. So far, I have
vnoremap af :<C-U>silent! normal! T<Tab>vf<Tab><CR>
omap af :normal Vat<CR>
vnoremap if :<C-U>silent! normal! T<Tab>vt<Tab><CR>
omap if :normal Vit<CR>
The problem is with the first and the last field because those lack a tab on one side. I've experimented with ?[\t\n]?e+1
and search()
instead of T<Tab>
(and analogously for the ending), using various patterns for defining text objects that I've found online, but nothing seems to work.
EDIT: Eventually I came up with this, but I can't help the feeling it's an overkill. If someone comes up with a nicer solution, I'll be very happy to learn.
function! TsvField (inner)
" first field
if getline('.')[:col('.')-1] !~ "\t[^[:blank:]]"
execute a:inner ? "normal ^vt\t" : "normal ^vf\t"
" last field
elseif getline('.')[col('.')-1:] !~ "\t"
execute a:inner ? "normal T\tv$h" : "normal F\tv$h"
" middle field
else
execute a:inner ? "normal T\tvt\t" : "normal T\tvf\t"
endif
endfunction
vnoremap if :<C-U>call TsvField(1)<CR>
omap if :normal vit<CR>
vnoremap af :<C-U>call TsvField(0)<CR>
omap af :normal vat<CR>
EDIT 2: After some more testing I discovered that [^\s]
does not match the letter s
, so the first if
fails when one of the fields begins with this letter. Replacing [^\s]
with \S
fixes this particular case – while breaking all the others. It seems [^[:blank:]]
might be the answer.
?^\|\t<CR>
might be able to replaceT<Tab>
, and then you would do something likelv/$\|\t<CR>h
. Untested, but maybe enough to help you get the rest of the way there?Vat
andVit
looks wrong; probably you meanvat
/vit
(though the actualat
/it
mappings throw away the original visual mode, so it probably works)normal!
, since that's how you structured the original.?
and/
are normal commands (derived from the original use for denoting ranges)