tags:

views:

186

answers:

2

I'd like to create a vim function/command to insert an XSD style timestamp. Currently, I use the following in my vimrc file:

nmap <F5> a<C-R>=strftime("%Y-%m-%dT%H:%M:%S-07:00")<CR><Esc>

I'd like to use the Perl code:

use DateTime;
use DateTime::Format::XSD;
print DateTime->now(formatter => 'DateTime::Format::XSD', time_zone => 'America/Phoenix');

But I don't know where to start. I'm aware that I can define a function that uses Perl. Example:

function PerlTest() 
perl << EOF
  use DateTime;
  use DateTime::Format::XSD;
  print DateTime->now(formatter => 'DateTime::Format::XSD', time_zone => 'America/Phoenix');
EOF

But when I changed my vimrc to the following, I didn't get what I expected:

nmap <F5> a<C-R>=PerlTest()<CR><Esc>

Could someone point me in the right direction for implementing this? This is the first time I've tried to write functions in vim. Also, I'm using vim 7.2 compiled with perl support.

A: 

If you're happy with running an external Perl script you can try this:

:map <F5> :let @a = system("perl script.pl")<cr>"ap

this runs the command perl script.pl (adjust depending on paths), captures its output in register @a and pastes it at cursor position.

kemp
+4  A: 

First off, you'll want to take a look at :help if_perl for the general information about using Perl from within Vim.

For this specific question, I don't think the same approach of entering insert mode and evaluating an expression is the best option. It doesn't look like the language bindings have a way to return a value like that.

What you can do instead is to have the function get the current line, put the time string at the appropriate place, and set the current line again.

fun! PerlTest()
    perl << EOF
    use DateTime;
    use DateTime::Format::XSD;
    my ($row, $col) = $curwin->Cursor();
    my ($line) = $curbuf->Get($row);
    substr($line, $col + 1, 0,
           DateTime->now(formatter => 'DateTime::Format::XSD',
                         time_zone => 'America/Phoenix'));
    $curbuf->Set($row, $line);
EOF
endfun

Then your map would simply be nnoremap <F5> :call PerlTest()<CR>.


One issue I've noticed with the above is that it doesn't work well if the line contains characters where 1 byte != 1 column (i.e., tabs, multi-byte characters, etc.). I've played with various ways of trying to fix that, but none of them seem to work very well.

The problem is that there's no easy way to map from Vim's cursor position to a position in the string that represents the cursor's current line.

A different approach, which avoids this problem, is to just use the Perl interface to get the data and then paste the data from Vim.

fun! PerlTest()
    let a_reg = getreg('a', '1')
    let a_reg_type = getregtype('a')
    perl << EOF
    use DateTime;
    use DateTime::Format::XSD;
    my $date = DateTime->now(formatter => 'DateTime::Format::XSD',
                             time_zone => 'America/Phoenix');
    VIM::Eval("setreg('a', '$date', 'c')");
EOF
    normal "ap
    call setreg('a', a_reg, a_reg_type)
endfun

nnoremap <F5> :call PerlTest()<CR>
jamessan