Saturday, November 24, 2007

Extract variable in Vim

I have automated the extract variable refactoring in Vim. Well, at least for languages where you don't have to declare types. This includes Python, the language I use at work. It can also be used with Ruby and other dynamic languages, as well as with languages featuring type inference, like Nemerle and Haskell.

Select (in visual mode) a value you want to extract, and press ctrl-e. This will pop up a dialog asking for the name of the variable. A line with the assignment will be added above and all occurrences of the value will be replaced with the given name. The replacement will happen only in the lines after the assignment.

To enable it, copy the snippet to your vimrc file.

function! ExtractVariable()
    let name = inputdialog("name please")
    if name != ""
        execute "normal O" . name . " = "
        normal p
        execute (line('.')+1) . ",$s/" . escape(getreg('"'), '/\') . "/" . name . "/" 
    endif
endfunction

noremap <c-e> y:call ExtractVariable()<cr>

Monday, October 22, 2007

Distributed build

I have created a set of scripts to run tests on many computers at the same time. This allows us to commit much sooner than before. You can read more about distributed build and our build system in general in Michael Foord's blog post.

The basic idea is to store the names of the tests to run in a database, and copy the source tree to a couple of machines. A machine would sign up for one test at a time and report results.

Friday, September 28, 2007

Swap parameters script for Vim

I have published a script for Vim. The script is written in python. The job of the script is to swap parameters. A user can swap a parameter under the cursor with the next one (gs) or with the previous one (gS). One can also use this command preceded with count. If I wanted to swap first parameter of a function with the third, I would type 2gs.

The possibility of writing scripts for vim in python not only made it very easy for me to write that script, but also allowed for easy testing. I used py.test framework and made a suite of quite literate tests. Below is a beautified set of use/test cases generated from the tests.

Left column contains the line before typing gs, the right shows the result. Position of the cursor is depicted with green background.

fun(parm1, parm2) fun(parm2, parm1)
fun(parm1(), parm2) fun(parm2, parm1())
fun(parm1(), parm2) fun(parm2, parm1())
fun(parm1(arg,arg2), parm2) fun(parm2, parm1(arg,arg2))
fun(parm1(arg,arg2), parm2) fun(parm2, parm1(arg,arg2))
fun(parm1(arg,arg2), parm2) fun(parm2, parm1(arg,arg2))
fun(parm1(arg, arg2), parm2) fun(parm2, parm1(arg, arg2))
fun(arg1, arg2, arg3) fun(arg1, arg3, arg2)
array[arg1, arg2] array[arg2, arg1]
fun(parm1[], parm2) fun(parm2, parm1[])
fun(parm1[], parm2) fun(parm2, parm1[])
fun(parm1, array[]) fun(array[], parm1)
fun(a,b) fun(b,a)
[(p1, p2), p3] [p3, (p1, p2)]

Test cases for gS -- swap backwards.

fun(parm2, parm1) fun(parm1, parm2)
fun(parm2, parm1()) fun(parm1(), parm2)
fun(parm2, parm1()) fun(parm1(), parm2)
fun(parm2, parm1(arg,arg2)) fun(parm1(arg,arg2), parm2)
fun(parm2, parm1(arg,arg2)) fun(parm1(arg,arg2), parm2)
fun(parm2, parm1(arg,arg2)) fun(parm1(arg,arg2), parm2)
fun(parm2, parm1(arg, arg2)) fun(parm1(arg, arg2), parm2)
fun(arg1, arg2, arg3) fun(arg2, arg1, arg3)
fun(arg1, arg2, arg3) fun(arg1, arg3, arg2)
array[arg2, arg1] array[arg1, arg2]
fun(parm2, parm1[]) fun(parm1[], parm2)
fun(parm2, parm1[]) fun(parm1[], parm2)
fun(array[], parm1) fun(parm1, array[])
fun(b,a) fun(a,b)

In the tests I mark the position of the cursor by enclosing a character with || symbols. Here is the first test.

  5     def test_simple_case(self):
  6         self.assertSwaps("fun(par|m|1, parm2)",
  7                             "fun(parm2, parm|1|)")

Monday, August 27, 2007

What happens during MessageBox.Show()?

Last Friday my pair-programming partner said that System.Windows.Forms.Timer has reentrance problem. By reentrance I mean a situation where during processing of one tick generated by a timer, another tick is triggered and now both are processed simultaneously (which was bad in our case). For this to happen the processing of some tick needs to take longer than the interval set on the timer. After I expressed my doubts about winforms timer having this issue, he skilfully put together an example, sth like the following.

 1 #pragma indent
 2
 3 using System.Windows.Forms
 4 using Nemerle.IO
 5
 6 def form = Form()
 7 def timer = Timer()
 8 timer.Interval = 5000
 9 timer.Tick += (_,_) => print(MessageBox.Show("bum!"))
10 form.Shown += (_,_) => timer.Start()
11 Application.Run(form)

The code is written in the Nemerle programming language, but that is not important. In line 9 a function is defined and attached to handle the tick event. As a consequence, on each tick the following code is executed. print(MessageBox.Show("bum!")) I used the print statement just to make it obvious that this function doesn't return until user closes the msgbox.

Anyway, I was certain that it will prove there is no reentrance problem. However contrary to my expectations, those unbearable message boxes started to pop up, stacking on one another.

Now a little bit about the winforms timer. The handlers attached to the tick event are executed on the GUI thread. Each time the interval elapses, the timer adds an "execute handlers attached to me" event to the event queue. This should guarantee no reentrance since events on the queue are processed one by one.

Processing of an event triggered by the timer can't complete until the messagebox closes. So, it seems that with each new messagebox we have new event being processed. How can that be?

Well, there is a static method on Application class, called DoEvents. You can call it from inside of a method handling some event. Tha MessageBox.Show must be doing sth similar. I wasn't able to reflect into that, because the displaying of the messagebox is eventually delagated to a non managed code. It looks sth like the following but I have deleted some lines.

 1 Application.BeginModalMessageLoop();
 2 try
 3 {
 4     result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, handle), text, caption, type));
 5 }
 6 finally
 7 {
 8     Application.EndModalMessageLoop();
 9 }
10 return result;

As far as I can tell BeginModalMessageLoop() should be rather named PrepareForModalMessageLoop(), because what it does is mainly to disable (gray out) all the forms in the application, EndModalMessageLoop(), on the other hand, enables them back.

I guess that the MessageBox.Show handles events from the queue until there is a message that causes the msgbox to close, upon which the Show method finally returns the appropriate DialogResult value.

CLR Debugger confirms or at least doesn't contradicts my assumption. I attach a fragment of a stack trace from execution of the example. out.exe!Main._N__N_l64198062 identifies the function handling the tick event. Notice that the second call is in the control flow of the first.

...
out.exe!Main._N__N_l64198062(object _N_u1626 = {Interval = 5000}, System.EventArgs _N_u1627 = {System.EventArgs}) Line 12 + 0xc bytes Unknown
System.Windows.Forms.dll!System.Windows.Forms.Timer.OnTick(System.EventArgs e) + 0x17 bytes
System.Windows.Forms.dll!System.Windows.Forms.Timer.TimerNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x36 bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 275, System.IntPtr wparam, System.IntPtr lparam) + 0x75 bytes
[Native to Managed Transition]
[Managed to Native Transition]

System.Windows.Forms.dll!System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window owner = null, string text, string caption, System.Windows.Forms.MessageBoxButtons buttons, System.Windows.Forms.MessageBoxIcon icon, System.Windows.Forms.MessageBoxDefaultButton defaultButton, System.Windows.Forms.MessageBoxOptions options, bool showHelp) + 0x1f8 bytes
System.Windows.Forms.dll!System.Windows.Forms.MessageBox.Show(string text) + 0x26 bytes
out.exe!Main._N__N_l64198062(object _N_u1626 = {Interval = 5000}, System.EventArgs _N_u1627 = {System.EventArgs}) Line 12 + 0xc bytes Unknown
...

line used to compile the example
ncc -r system.windows.forms.dll timer.n