Start gVim with Tab Pages or Windows

I’ve recently started using gVim more when I work with text files, and it makes me sad that I ever quit using it.  I’ll write more about why I think Vim is awesome in another post; this is another “It took me a few minutes to find this so I hope I help someone else” post.

If you want to start gVim with tabs, the command-line switch is -p[n]. If you don’t specify n, the default is "1 tab per file you specify". If you specify n < w files, the files that don’t have tabs will be opened as buffers (see :help buffers for that; it’s a good topic for another post.) I’m not sure what happens for n > files; I assume it opens n tabs.

If you want to start gVim with windows, the command-line switch is -o[n]. It behaves like the tabs command-line switch.

For those that prefer examples (I know I do!):

gvim foo.txt bar.txt baz.txt
gVim opens with foo.txt in the primary buffer and the other files in other buffers.
gvim -p foo.txt bar.txt baz.txt
gVim opens with 3 tabs, one tab for each file.
gvim -p2 foo.txt bar.txt baz.txt
gVim opens with 2 tabs. foo.txt and bar.txt are in tabs, baz.txt is in a buffer.
gvim -o foo.txt bar.txt baz.txt
gVim opens with 3 windows, split who knows how.

The rest of the cases seem trivial to understand with these explanations.

Ordering of numeric conditional tests

I’m reading Code Complete and something caught my eye in chapter 19. At first I wanted to argue with it, but the more I thought about it the more I liked it.

The idea concerns the situation where you need to test if a variable satisfies an inequality, for example, if you need to know if min < n < max. I’ve always done it like this:

if (x > min) && (x < max)
// -or-
if (x < lower) || (x > higher)

I did it this way because I liked having the comparison variable on the left and the constant on the right. McConnell suggested that the organization should look more like the inequality:

if (min < x) && (x < max)
// -or-
if (x < lower) || (higher < x)

I initially rebelled against this part of the chapter simply because it was not what I liked, but the more I think about it the more I like it. Almost every time I have to code an inequality like this I make a mistake, and I always write extra tests for these cases. I think if I start writing the conditions this way, I can increase the probability that I’ll get it right the first time.

Working with Enumerations in PowerShell

Two posts in one day should show you I’m excited about PowerShell.

I wanted to update the script from my last post to find my My Documents folder so if I hop to Vista I won’t get an error, and I found it slightly difficult to figure out how to use Environment.GetFolderPath because I got a lot of errors trying to use the Environment.SpecialFolder enumeration.  It turns out if you pass the string value for the enumeration value you want, you get basically what you want:

$myDocs = $myDocs = [System.Environment]::GetFolderPath("MyDocuments")
Set-Location $myDocs
Set-Variable -name home -value $myDocs -force

Apparently, you can also use flags enumerations.

Setting $home in PowerShell

Lately I’ve been getting acquainted with Windows PowerShell, which is something Windows has needed for a long time.  Finally, there’s a real shell for Windows that supports scripting in a format other than horrific batch files.

At work, for whatever reason the powers that be have set our %HOMEDRIVE% and %HOMEPATH% environment variables to a networked drive.  This would be a good idea, but the problem is this drive has a pretty low quota applied and I managed to fill it up within a week or so.  Basically, people use it as a convenient public share directory.  I mention this because changing the environment variables in this way has caused me much grief.  cmd.exe defaults to %HOMEDRIVE%\%HOMEPATH% when it starts, so every time I open a command prompt I end up in this stupid useless directory.  PowerShell is not immune to this either.

I noticed that my PowerShell always started here and that the $home variable seemed set to it as well.  I set up a profile to change my location to the correct place, but had trouble setting the value of the $home variable since it is read-only (or constant, according to the error message).  Luckily, there’s a way to tell PowerShell, "I know what I’m doing." and the Set-Variable cmdlet takes a -force parameter.  So in the end, my script ended up looking something like this:

Set-Location "C:\Documents and Settings\username\My Documents"
Set-Variable -name home -value "C:\Documents and Settings\username\My Documents" -force

I could probably make this more OS-agnostic by using the .NET Environment class, but that’s a project for another day.

Programmatically Creating Windows File Associations

A user on the VB Development Forum I’ve visited for a few years asked a question about creating file associations.  I chuckled and assumed that a Google search would return a wealth of information about the subject.  I was wrong; by my fifth combination of search terms I had found one site that had the minimal steps to get it done, and it wasn’t even correct.  The rest of the results were happy to discuss using the File Types tab of the Folder Options dialog to accomplish the task; this is not programmatic and not what the user wanted.  I remembered having a heck of a time trying to figure out how to make an association for all files (so I could have a right-click->Edit with hex editor option) so I decided I’d write up what I know.  Shame on you, internet, for letting the information get buried.

To start off, my example is technically incorrect.  I’m working with HKCR, but apparently the content here is generated based off of HKCU\Software and HKLM\Software. I assume this is so you can have per-user and global associations; I wrote my example using HKCR directly but you should probably use one of these two; please understand when I say HKCR I mean one of the other two locations.

I found the File Associations documentation, but haven’t had time to look over it properly.  For now, this should do.  In order to make it so that when you double-click a file your application opens you need three things:

  1. Your application should support opening a file from the command-line in some way.
  2. You need a registry key to associate a file type with the extension.
  3. You need a registry key to associate a program with the file type.

The first one is up to you; I can’t tell you how to do it.  The registry key to associate a file type with an extension is pretty easy.  File extensions are listed in HKCR under keys named after the extension (including the period!) The value of these keys is a string that defines the file type associated with the extension.  I tend to not use spaces in these, but I have no idea if spaces are unsupported; it will end up being the name of a subkey later and I’m too lazy to check if spaces are supported in subkey names.  For example, text files have the extension txt, and the key that specifies the file type is HKCR\.txt.  It’s default value is txtfile.

Now that there’s a file type associated with the extension, you need a registry key that tells the shell how to handle operations on the file type.  There’s lots of things to implement here, but I only know the basics.  File types are declared in subkeys of HKCR named after the file type; their subkeys and default values vary based on exactly how the file type is to be handled.  All I’m going to discuss is handling the "open" verb, which is generally invoked by double-clicking a file in the shell.  To add support for this verb, a tree of subkeys need to be created under HKCR\filetype. Working with our Notepad example above, we see HKCR\txtfile\shell\open\command.  It has a value that represents the path to Notepad and the replacement string %1 for the file that was selected.

Example time

Suppose we want to associate Notepad with the file extension .zzz.  We’ll start by defining a key for the file type:

HKCR\.zzz
Value="MyEditorFile"

Now, we create the file type key and associate the "Open" verb with notepad:

HKCR\MyEditorFile\open\shell\command
Value="%SystemRoot%\system32\NOTEPAD.EXE %1"

Note that though there are no quotes in the value for the open key, I’ve had trouble in the past when this string didn’t have anything that could possibly contain a space quoted. For example, I’m used to using:
"C:\Program Files\My Program\program.exe" "%1"
I’m not sure why Notepad’s special and doesn’t require this.   That’s really all I have to say on this topic for now; I am still looking over the documentation.

Getting Soft Word Wrap in gVim and Emacs

I really don’t like most Windows text editors.  I use gVim for all of my text editing.  Everyone extols the virtues of Notepad++ and several other text editors, but they miss some basic features that really make text editing a lot more fun.  gVim lets me move around the file word-for-word without nasty Ctrl+this or that combos that require me to put my hands all over the keyboard.  I can get to very specific parts of a text file very quickly with gVim, whereas all Windows text editors force me to use the mouse.  gVim has the added bonus that it’s really rare to find a *NIX system without it, so I’m able to happily edit text on any machine.

The one thing Windows text editors always get right that I could never get gVim to do is soft word wrapping.  Notepad does this perfectly (so do web input controls.)  When you hit the end of the line, the text wraps to the next line, but when Notepad saves the file, all of the text is on one line.  gVim uses hard word wrapping by default: When you hit the end of the line in gVim, one of three things happens based on your word-wrapping options:

  1. The text scrolls and you’re in horizontal scroll unhappyland.
  2. gVim happily inserts a hard line break into your file.
  3. gVim wraps the text, but leaves a marker to indicate that this is a logical line rather than a physical line.

I hate #1.  I want to be able to see all the text in the file except in very rare circumstances.  #2 sucks when I’m writing something that’s ultimately bound for another program with no facilities for saving, like our issue tracking software; it really stinks
when all of your text has unnatural breaks because
the character width of the editor is nowhere near
as big as the web page.

This leaves #3 as the setting I use most frequently.  This gets annoying for scrolling though; my mind wants to jump from the "first" line to the "second", but this is a horizontal operation rather than the vertical operation that the display suggests.  It always takes me several tries.  I tried for a long time to find ways to get soft word wrapping in gVim, but decided it was impossible and started down the dark path of Emacs (which is actually pretty cool, but I still like gVim better!)

I found that Emacs performed much like gVim, but the way it did its wrapping like situation #3 was much more tolerable.  Still, I wanted things to work like in Notepad.  I found a blog post about enabling soft word wrapping in Emacs (M-x longlines), and it worked like a charm.  Still, I had a lot of muscle memory invested in gVim, so I decided to give it one last Google search.  gVim tip #989 is enabling word wrap without line breaks, which works like a charm.

I’m still kicking around the idea of converting to Emacs, though.  I like gVim’s "your hands shouldn’t leave the home row" philosophy, but it really seems like Emacs has better support for things like HTML and XML.

Making a control that accepts arrow keys

I’m working on a project with a custom control on which I want to implement the arrow keys.  I was completely mystified as to why I wasn’t getting KeyUp events when I pressed the arrow keys. I started using Reflector to hunt for where the event was raised in controls I was familiar with, and I stumbled upon the Control.IsInputKey method.  What does this method do? The remarks told me what I needed to know.

Call the IsInputKey method to determine whether the key specified by the keyData parameter is an input key that the control wants. This method is called during window message preprocessing to determine whether the specified input key should be preprocessed or sent directly to the control. If IsInputKey returns true, the specified key is sent directly to the control. If IsInputKey returns false, the specified key is preprocessed and only sent to the control if it is not consumed by the preprocessing phase. Keys that are preprocessed include the TAB, RETURN, ESCAPE, and the UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW keys.

I had no idea this existed. It turns out to get events from these keys in any control, you have to override this method and return true. Here’s an example control in C# 3.0 to demonstrate (conversion to C# 2.0 is trivial; just make a backing field for the property). When it has focus (you’ll know because it is white instead of gray), if you push an arrow key the text name of the key you pressed is displayed. If you press any other key, you’ll get “Unknown”. The documentation does seem a little off, as I’m not doing anything special for Esc or Return but they still cause the events to be raised. Still, if you set CustomInput to false, you won’t ever get an arrow key message displayed (the arrow keys seem to move the focus).

using System;
using System.Windows.Forms;
using System.Drawing;

namespace TestIsInput
{
    public class InputField : Control
    {
        public InputField()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.Selectable, true);
        }

        public bool CustomInput
        {
            get;
            set;
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if (CustomInput)
            {
                bool isInput = false;

                switch (keyData)
                {
                    case Keys.Up:
                    case Keys.Down:
                    case Keys.Left:
                    case Keys.Right:
                        isInput = true;
                        break;
                    default:
                        isInput = false;
                        break;
                }
                return isInput;
            }
            else
            {
                return base.IsInputKey(keyData);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Brush backBrush = this.Focused ? Brushes.White : Brushes.LightGray;

            e.Graphics.FillRectangle(backBrush, this.ClientRectangle);
            ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle, Border3DStyle.Sunken);
            StringFormat f = new StringFormat(StringFormatFlags.NoWrap);
            f.Alignment = StringAlignment.Center;
            f.LineAlignment = StringAlignment.Center;

            e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, (RectangleF)this.ClientRectangle, f);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            switch(e.KeyCode)
            {
                case Keys.Up :
                    this.Text = "Up";
                    break;
                case Keys.Down :
                    this.Text = "Down";
                    break;
                case Keys.Left :
                    this.Text = "Left";
                    break;
                case Keys.Right :
                    this.Text = "Right";
                    break;
                default :
                    this.Text = "Unknown";
                    break;
            }

            this.Invalidate();
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            this.Invalidate();
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            this.Invalidate();
        }
    }
}

So, if you need to accept input from one of those keys, keep in mind that you need to override IsInputKey. This was completely new to me!

Mixing numbers and replacement strings

This is yet another thing that I found took me too long to find on Google. I’m using the .NET regular expressions, and looking to replace values in a string with numbers, and I need to use the special $1 symbols to keep some of my capture groups around.

So, suppose I have the text:
something.2005.txt

The pattern:
(.*)2005(.*)

And the replacment string:
$12008$2

This produces the undesirable result $12008.txt. Why? Well, the $1 has to be escaped in some way. Whatever it is that scans for variables is obviously interpreting this as $12008 instead of $1; either that’s invalid or the group doesn’t exist so it just uses the text. Personally I think I should be getting “2008.txt” in this case, as a raw $ should take $$ to create, but I won’t complain.

The solution? Surround the number of the group index in brackets:
${1}2008$2

Windows ComboBox keyboard shortcuts

I use the keyboard a ton, and I found myself almost twice a week having to look up the keys to drop the menu in a ComboBox control. The right stuff tends to be buried in large documents, and I wasted a lot of time digging through them. No more! Now I can just check my blog, and hopefully Google will index it and others can find it more quickly.

F4 works, and so does ALT + DOWN. I like F4 better, because it can’t accidentally activate the window’s menu.