Living in the past with Vim, for fun and profit

Sat 09 May 2015 | tags: 47
A recent news article on HN (HN: Emacs and Vim, original article here) caused me to pause to reflect on my choice of editors. I use the GUI version of Vim for most of my serious editing, but I will also use Eclipse for editing Java, HTML, and JSPs and MS Visual Studio for C++/C#. Why so many different editors?

Each one is good at different things, which is why I use them all. But I keep coming back to Vim when I have to do something harder. For example, regular expression support in Vim is way better than in the other editors. Let's say I need to extract the servlet names in a Tomcat access_log file like this:
127.0.0.1 - - [18/Oct/2014:15:29:53 -0400] "GET /examples/servlets HTTP/1.1" 302 -

127.0.0.1 - - [18/Oct/2014:15:29:53 -0400] "GET /examples/servlets/ HTTP/1.1" 200 5343
127.0.0.1 - - [18/Oct/2014:15:29:53 -0400] "GET /examples/servlets/images/code.gif HTTP/1.1" 200 292
127.0.0.1 - - [18/Oct/2014:15:29:53 -0400] "GET /examples/servlets/images/return.gif HTTP/1.1" 200 1231
127.0.0.1 - - [18/Oct/2014:15:29:53 -0400] "GET /examples/servlets/images/execute.gif HTTP/1.1" 200 1242
127.0.0.1 - - [18/Oct/2014:15:30:08 -0400] "GET /examples/servlets/servlet/HelloWorldExample HTTP/1.1" 200 359
127.0.0.1 - - [18/Oct/2014:15:30:11 -0400] "GET /examples/servlets/helloworld.html HTTP/1.1" 200 2612
127.0.0.1 - - [18/Oct/2014:15:30:25 -0400] "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" 200 693
127.0.0.1 - - [18/Oct/2014:15:30:27 -0400] "GET /examples/servlets/reqinfo.html HTTP/1.1" 200 3674
127.0.0.1 - - [18/Oct/2014:15:30:51 -0400] "GET /examples/servlets/reqheaders.html HTTP/1.1" 200 2304
127.0.0.1 - - [18/Oct/2014:15:30:56 -0400] "GET /examples/servlets/servlet/RequestHeaderExample HTTP/1.1" 200 1067
127.0.0.1 - - [18/Oct/2014:15:31:04 -0400] "GET /examples/servlets/reqparams.html HTTP/1.1" 200 4650
127.0.0.1 - - [18/Oct/2014:15:31:35 -0400] "GET /examples/servlets/servlet/RequestParamExample HTTP/1.1" 200 657
127.0.0.1 - - [18/Oct/2014:15:31:56 -0400] "GET /examples/servlets/cookies.html HTTP/1.1" 200 2741
127.0.0.1 - - [18/Oct/2014:15:31:59 -0400] "GET /examples/servlets/servlet/CookieExample HTTP/1.1" 200 637
127.0.0.1 - - [18/Oct/2014:15:32:02 -0400] "GET /examples/servlets/sessions.html HTTP/1.1" 200 3267

So I don't care about most of the content except the timestamp and the servlet name. The following regex solves the problem: s/^.*[\(.*\)]\.*servlets\(.*\) H.*$/\1 \2/

I know that looks like gibberish to someone who hasn't used regexes much, but here is what it produces:
18/Oct/2014:15:29:53 -0400 

18/Oct/2014:15:29:53 -0400 /
18/Oct/2014:15:29:53 -0400 /images/code.gif
18/Oct/2014:15:29:53 -0400 /images/return.gif
18/Oct/2014:15:29:53 -0400 /images/execute.gif
18/Oct/2014:15:30:08 -0400 /servlet/HelloWorldExample
18/Oct/2014:15:30:11 -0400 /helloworld.html
18/Oct/2014:15:30:25 -0400 /servlet/RequestInfoExample
18/Oct/2014:15:30:27 -0400 /reqinfo.html
18/Oct/2014:15:30:51 -0400 /reqheaders.html
18/Oct/2014:15:30:56 -0400 /servlet/RequestHeaderExample
18/Oct/2014:15:31:04 -0400 /reqparams.html
18/Oct/2014:15:31:35 -0400 /servlet/RequestParamExample
18/Oct/2014:15:31:56 -0400 /cookies.html
18/Oct/2014:15:31:59 -0400 /servlet/CookieExample
18/Oct/2014:15:32:02 -0400 /sessions.html

That's exactly what I want! It only took one command in Vim. I'm sure you can get your favorite editor to do it too, but Vim has always worked well for me.

Another example: Let's say I need to generate some repetitious code because SQL Server's tsql language doesn't really support arrays, and someone else generated column names with numbers at the end.
INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)

SELECT NAME01, ADDR01, ZIP01
FROM dbo.OtherBadTable
WHERE ID='01'

INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)
SELECT NAME02, ADDR02, ZIP02
FROM dbo.OtherBadTable
WHERE ID='02'

The task at hand is to copy data from OtherBadTable to BadTable (for IDs 01 to 09). Yes, I know this table has a terrible design, but it wasn't my choice, and I cannot change it. I could use dynamic SQL, but the column names are fixed, and there are only 9 of them.

Here's a VIM solution. Start with the template below:
INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)

SELECT NAMEXX, ADDRXX, ZIPXX
FROM dbo.OtherBadTable
WHERE ID='YY'

Next, make 9 more copies with ggVGy (select all lines) and then pasting 9 times with 9P:
INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)

SELECT NAMEXX, ADDRXX, ZIPXX
FROM dbo.OtherBadTable
WHERE ID='YY'

INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)
SELECT NAMEXX, ADDRXX, ZIPXX
FROM dbo.OtherBadTable
WHERE ID='YY'

(etc.)

Finally, use these commands to make a list: let i=1 | g/XX/s//\='0'.i/g | let i=i+1 and then let i=1 | g/YY/s//\='0'.i/g | let i=i+1 . The final output is:
INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)

SELECT NAME01, ADDR01, ZIP01
FROM dbo.OtherBadTable
WHERE ID='01'

INSERT INTO dbo.BadTable (NAME, ADDR, ZIP)
SELECT NAME02, ADDR02, ZIP02
FROM dbo.OtherBadTable
WHERE ID='02'

(etc.)

There! 50 lines of code generated with only 4 Vim commands. Obviously, it's easy to extend this method to as many column names as you need. My point is just that this is a thankless, boring typing task, which can be automated easily in Vim.

I saved the best for my next post, but here's an introduction. Macros in Vim are very powerful. You can record a macro from almost any set of Vim commands and then replay the macro to make a huge number of changes to file painlessly.

Download Vim today and give it a try!

blogroll