I have written my Word add-in using VBScript regex v5.5 to parse the command. Feedback and contributions welcome! As of 2019-02-15, the add-in works in Word 2007–2016. Edit As of 2020-02-13, 64-bit Word is also supported.
I am largely following the model in the grammar @Mass kindly linked to. As of v0.2.9, my regex is:
^(([1-9][0-9]*)?((([HMLGhjklwbWB\x28\x29\x7b\x7d]|g?[eE0\^\$]|[fFtT](.))|(gW)?g?[\*#]|g?[pP])|([cdyv])([1-9][0-9]*)?(([\[\]])?([ai])([wWsp])|[fFtT](.)|[HMLGhjklwbWB\x28\x29\x7b\x7d]|g?[eE0\^\$])))$
with zero-based submatch numbers:
1 The initial count
3 Intransitive verb (e.g., 0, w, f<char>)
4 Motion for an intransitive verb
5 The <char> of an f, F, t, or T
7 Transitive verb (e.g., operators d, c, y)
8 The count after the operator
9 The motion or text object ("target")
10 vim-ninja-feet* [ or ]
11 a or i if you are selecting a text object
12 For a and i motions, which type of object is being selected (w, W, s, p)
13 For f, F, t and T motions, which character is being used
* vim-ninja-feet
Registers are still not supported.
The regex is complicated enough that I wrote a Perl script to generate the regex from a text description of it. That description is below. Run ./re2vba.pl vim-regex.txt
to generate the regex and submatch-number assignments on stdout.
In the description, (?<name>...)
represents a named submatch, and (?<=piece-name)
pulls in a named piece of the regex. Comments (#...
or (?#...)
) and trailing whitespace (after stripping comments) are ignored.
# Main pattern
main ^( (?# Note: registers not yet implemented)
(?<count1>[1-9][0-9]*)?
(?:
(?<=intrans)
|
(?<=trans)
)
)$
# Motions that don't take arguments (although they may take counts).
# A motion of "0" is special-cased in the parsing code to keep the regex
# clean. This regex, after backtracking, matches "10" as a count of "1"
# followed by a "0" motion. We special-case rather than handling that here.
noarg-motion [HMLG
hjkl
wbWB
\x28\x29\x7b\x7d]|
(?# 28/29 are parens, move sentence )
(?# 7b/7d are braces, move paragraph )
g?[eE0\^\$] (?# gw, gb, etc. are not motions.)
(?# ^ Note: 0 is special-cased in the parsing code.)
# Intransitive, including motions. These can take a count, namely ?<count1>.
intrans (?<iverb>
(?<imotion> (?# motions)
(?<=noarg-motion)|
[fFtT](?<itext>.)
)|
(gW)?g?[\*#]| (?# searches)
g?[pP] (?# pastes)
)
# IVERB: what to do
# ITEXT: character to jump to
# Text-object selection ([ai][something])
textobj (?<ninja>[\[\]])? (?# Ninja-feet marker)
# https://github.com/tommcdo/vim-ninja-feet
(?<tobj_range>[ai]) (?# Introducer)
(?<objtype>[wWsp]) (?# Type of text object)
# Transitive: verb, count, object, object type, text
trans (?<tverb>[cdyv]) (?# TVERB: what to do)
(?<count2>[1-9][0-9]*)? (?# Count after the operator)
(?<target> (?# What to work on)
(?<=textobj)| (?# Text objects)
[fFtT](?<ttext>.)| (?# Motions with an argument)
(?<=noarg-motion)
)
Note 1: Just like in Vim, v
selects the text moved over. However, Word is essentially always in a combination of normal and visual modes, so v
doesn't leave you in any special mode.
Note 2: I don't need the c
intransitive verb for one-offs, since you are back in Word (i.e., insert mode) after a d
. However, I am working on a loop version that will leave you in Normal mode after a d
but not after a c
(#3).