How to comment a VIM macro
Is it possible to comment a macro and r开发者_开发知识库eplay it.
Example
instead of
dddwj
I would like to comment and execute following fragment
dd # Delete line
dw # Delete word
j # Move to next line
Some background
We use PICT to generate testcase inputs (All Pair testing). As this is an iterative process, the macro for generating code needs tweaking between subsequent runs. It's hard to modify a macro when everything is on one line, without comments.
The output of a PICT run might be something like this:
1 cInstallationX Pu380
2 cInstallationY U400
wich can be converted to testcases with a macro
procedure TWatchIntegrationTests.Test1;
begin
//***** Setup
builder
.withInstallation(cInstallationX)
.withIsotope(Pu380)
.Build;
//***** Execute
CreateAndCollectWatches;
//***** Verify
VerifyThat
.toDo;
end;
procedure TWatchIntegrationTests.Test2;
begin
//***** Setup
builder
.withInstallation(cInstallationY)
.withIsotope(U400)
.Build;
//***** Execute
CreateAndCollectWatches;
//***** Verify
VerifyThat
.toDo;
end;
I don't know a good way of doing this with macros, but there are a few options that I can see that might help:
Heavy use of 'normal'
This is the closest to your macro option, but not very nice: make your saved file look like this:
" Delete line
normal dd
" Delete word
normal dw
" Move to next line
normal j
Complicated Substitution
This makes use of regular expressions, but makes those regular expressions be well commented (this is based on your actual example).
let pattern = '^' " Start of line
let pattern .= '\(\d\+\)' " One or more digits (test number)
let pattern .= '\s\+' " Space or tab as delimiter
let pattern .= '\(\k\+\)' " Installation name
let pattern .= '\s\+' " Space or tab as delimiter
let pattern .= '\(\a\+\d\+\)' " One or more alphabetic characters, then one or more spaces (isotope)
let pattern .= '\s*$' " Any spaces up to the end of the line
let result = 'procedure TWatchIntegrationTests.Test\1;\r'
let result .= 'begin\r'
let result .= ' //***** Setup\r'
let result .= ' builder\r'
let result .= ' .withInstallation(\2)\r'
let result .= ' .withIsotope(\3)\r'
let result .= ' .Build;\r'
let result .= '\r'
let result .= ' //***** Execute\r'
let result .= ' CreateAndCollectWatches;\r'
let result .= '\r'
let result .= ' //***** Verify\r'
let result .= ' VerifyThat\r'
let result .= ' .toDo;\r'
let result .= 'end;\r'
exe '%s!' . pattern . '!' . result . '!'
Stick it in a function
Given that this is getting rather complicated, I'd probably do it this way as it gives more room for adjustment. As I see it, you want to split the line on white space and use the three fields, so something like this:
" A command to make it easier to call
" (e.g. :ConvertPICTData or :'<,'>ConvertPICTData)
command! -range=% ConvertPICTData <line1>,<line2>call ConvertPICTData()
" Function that does the work
function! ConvertPICTData() range
" List of lines producing the required template
let template = [
\ 'procedure TWatchIntegrationTests.Test{TestNumber};',
\ 'begin',
\ ' //***** Setup',
\ ' builder',
\ ' .withInstallation({Installation})',
\ ' .withIsotope({Isotope})',
\ ' .Build;',
\ '',
\ ' //***** Execute',
\ ' CreateAndCollectWatches;',
\ '',
\ ' //***** Verify',
\ ' VerifyThat',
\ ' .toDo;',
\ 'end;',
\ '']
" For each line in the provided range (default, the whole file)
for linenr in range(a:firstline,a:lastline)
" Copy the template for this entry
let this_entry = template[:]
" Get the line and split it on whitespace
let line = getline(linenr)
let parts = split(line, '\s\+')
" Make a dictionary from the entries in the line.
" The keys in the dictionary match the bits inside
" the { and } in the template.
let lookup = {'TestNumber': parts[0],
\ 'Installation': parts[1],
\ 'Isotope': parts[2]}
" Iterate through this copy of the template and
" substitute the {..} bits with the contents of
" the dictionary
for template_line in range(len(this_entry))
let this_entry[template_line] =
\ substitute(this_entry[template_line],
\ '{\(\k\+\)}',
\ '\=lookup[submatch(1)]', 'g')
endfor
" Add the filled-in template to the end of the range
call append(a:lastline, this_entry)
endfor
" Now remove the original lines
exe a:firstline.','.a:lastline.'d'
endfunction
Do it in python
This is the sort of task that is probably easier to do in python:
import sys
template = '''
procedure TWatchIntegrationTests.Test%(TestNumber)s;
begin
//***** Setup
builder
.withInstallation(%(Installation)s)
.withIsotope(%(Isotope)s)
.Build;
//***** Execute
CreateAndCollectWatches;
//***** Verify
VerifyThat
.toDo;
end;
'''
input_file = sys.argv[1]
output_file = input_file + '.output'
keys = ['TestNumber', 'Installation', 'Isotope']
fhIn = open(input_file, 'r')
fhOut = open(output_file, 'w')
for line in fhIn:
parts = line.split(' ')
if len(parts) == len(keys):
fhOut.write(template % dict(zip(keys, parts)))
fhIn.close()
fhOut.close()
To use this, save it as (e.g.) pict_convert.py
and run:
python pict_convert.py input_file.txt
It will produce input_file.txt.output
as a result.
First of all let me point out that @Al has posted several excellent solutions and I suggest you use those and not what I am about to post. Especially since that does not seem to work under all circumstances (for reasons I do not understand).
Having said that, the following seems to do what you want at least in this case. It assumes <Space>
in normal mode is not used to move the cursor around. Maps it to :"
where "
is the comment character for cmline mode. Which means <Space>
is the character that starts a comment in this case. The newline at the end stops the comment. The #
is just there to make it clearer we are dealing with comments. (^[
should be entered as a single escape character).
:nmap <Space> :"
iHallo wereld^[ # Insert text (in dutch, better change that)
Fe # Move backwards to e
x # Delete
; # Move to next e
ro # Change to o
Fa # Move backwards to a
re # Change to e
A!^[ # Add exclamation mark
精彩评论