My name is Michael Hutchinson and I'm a software engineer working for Xamarin on the MonoDevelop IDE and Mono for mobile platforms. Here you can find my journal, projects and photos.

Journal

Colour Completion

When I initially wrote the ASP.NET code completion, I added support for completing attribute values for enums and bools. More generalised completion using TypeConverter.GetStandardValues isn't so easy, since I don't want to load arbitrary types into the MD process, so I skipped this for now. However, I did special-case System.Drawing.Color, since it's used a lot.

This morning, on a suggestion from Aaron, I tweaked it to generate custom icons for each colour:

Screenshot of a code completion window showing appropriately coloured icons for each named colour.

In other news, I landed the new ASP.NET completion parser, which is much more robust, and is also able to generate a tree, so it will soon replace the DOM parser too. The new design also means I'll be able to unify it with the XML and Moonlight (XAML) addins.

I recently returned from a family holiday in Maine, and will post photos when I got them sorted out and uploaded to Flickr.

Whales, Beavers and Snakes (Oh My!)

My family is visiting me here in Boston, and over the Independence Day weekend we toured Western Massachusetts for a few days. I captured some interesting photos. Having finally convinced myself to purchase some nice lenses for my SLR, I am particularly pleased with this haul.

Humpback Whale

Boston Harbour

July 4th Procession

Beavers

Garter Snake

Progress on ASP.NET Source Editing

I have been making good progress on ASP.NET source editing, as evidenced by the following screenshot:

Screenshot of an ASP.NET file in MonoDevelop with error underlining, code folding and a path bar.

Yes, that is code folding, error underlining, and a path bar. However, the document outline pad isn't hooked up yet for ASP.NET[Update 2008/5/2: outlining implemented].

I've been getting a lot of practical experience with handwritten parsers recently. The ASP.NET addin has two parsers; the first builds a full DOM, and the second provides document path information very efficiently.

The path parser is designed to accept individual characters as the user types them, instantly providing information about the current context. This is very useful for code completion and smart indentation, and is obviously near-ideal for the path bar too. It's about as robust as I can make it, recovering effectively from most errors. However, I wrote a state-based XML parser where each state was a path object, which has proved rather unwieldy to extend cleanly for ASP.NET, so this may well get replaced soon.

The DOM parser was originally written for the CodeBehind field generation. It's based on Mono's ASP.NET tokeniser, which makes it rather fragile; when it encounters a syntax error, it dies. For its original use, this is sensible behaviour, but for a text editor it needs to be much more robust, so I intend to modify the path parser to build this DOM instead.

I plan to roll a lot of this back into the XML addin, which will provide a solid base for XAML completion for Moonlight apps.

Shiny Feature of the Day

Today I implemented an oft-requested feature for MonoDevelop: making the code completion and info tooltips transparent when the Control key is held down. This enables you to take a look at the code that the window's obscuring, without dismissing the popup:

Screenshot of a semi-transparent code completion window.

This feature's in MonoDevelop trunk, and can be tried out right away. You'll need GTK+ 2.12 or above (GTK# only needs to be 2.8 or above) and Compiz or any other compositing window manager. It's also available in my MonoDevelop trunk builds on the openSUSE build service.

This behaviour is encapsulated in a class that can be attached to any Gtk.Window, and handles a couple of nasty hacks and workarounds.

Miscellanea

Here's a quick summary of interesting things from the past few months that I haven't blogged. Unfortunately I can't give each topic the time it deserves, but I think that's better than not mentioning them at all.

GHOP Results

The Google Highly Open Participation contest finished, and was a great success, with 40 completed tasks, a great improvement over my earlier post. Of particular note was the huge number of rules implemented for Gendarme. I'd like to thank all of our successful students and their mentors, and congratulations to Dan Abramov, our Grand Prize winner. I'd also like to single out Andreas Noever, another absolutely stellar student who very narrowly missed out on the top spot.

Novell Hack Week

During Novell Hack Week I worked on an automatic error reporting system that I intend to use in MonoDevelop at some point. I got rather hung up on collecting as much system and process information as I could, so it unfortunately wasn't completed.

However, I did have fun "architecting" a client/server/webserver data channel, which I planned to re-use for several other purposes: reporting problems' solutions back to the user, and collecting application usage data. Now that I've seen how useful such data can be, in Jensen Harris's awesome presentation on how the Microsoft Office ribbon was developed, I'm particularly interested in having something similar in MonoDevelop.

Game Developer's Conference

Towards the end of February some of us on the Mono team went to the Game Developers Conference in San Francisco to promote Mono as a game scripting engine, and find out what games developers need to make this work for them. All in all it was great fun, and very successful. Miguel has a longer writeup.

I would be interested to find out what we could do to make Mono more attractive for gaming, both for embedding as a scripting engine, and as a portable runtime, and how this could be supported by MonoDevelop. One of the real strengths of Mono is the development tools, and I would like MonoDevelop to become part of the gaming development ecosystem. One suggestion would be an "Axiom Studio" addin for MonoDevelop as an alternative to XNA.

MonoDevelop 1.0 Release

After years of development, we released MonoDevelop 1.0 alongside Mono 1.9, and work is already well underway on the next release. I'm already working on ASP.NET code completion, which is coming along quite nicely. I've also updated and integrated Matt Ward's XML Editor into MonoDevelop trunk.

Google Summer of Code

MonoDevelop is once again taking part in the Google Summer of Code under Mono's umbrella. I really enjoyed mentoring Marcos last year, and I'm looking forward to doing some mentoring this year.

Visit to the UK

These past couple of weeks I was in the UK visiting family and friends. it was nice to see people again after 5 months in another continent!

An Apologetic Sneak Peek

Unfortunately, I have blogged very little in recent months. I shall follow this post with a summary of the things I've skipped blogging, but for now, I offer up the following tantalising screenshot by way of apology:

Screenshot of ASP.NET event handling code completion in MonoDevelop.

Planning ASP.NET Code Completion for MonoDevelop

Now that MonoDevelop 1.0 is on the verge of shipping, I have begun to plan the parser that will underpin the ASP.NET code completion and visual designer in upcoming versions of MonoDevelop. During a discussion with our ASP.NET expert Marek, I found out about an obscure ASP.NET feature that currently causes problems for Mono's ASP.NET parser, and is entirely counterintuitive to anyone with any XML knowledge.

Apparently, ASP.NET server tags are able to generate any part of the page that they desire, except for other ASP.NET constructs, and the following vilely ugly code is completely valid on .NET:

<div>
    <asp:Literal id="this_is_stupid" runat="server" Text="<p style='color:red'>" />Hello</ p>
</div>

When a developer does this, the document's XHTML DOM cannot be parsed at design time unless the parser knows the exact HTML fragment that will be output by each ASP.NET control, and hence this can make HTML completion incredibly difficult. Although the Mono ASP.NET stack needs to support this "feature" for compatibility with .NET, I am firmly of the opinion that anyone doing this should be poked with a sharp stick until they stop.

It makes no sense to support this kind of unbalanced syntax in MonoDevelop, as it will hugely increase the complexity and development time. Indeed, when I investigated this on Windows, I found that VS2008 considers it to be a warnable offence.

The downside to my decision is that it's probably not going to be possible to continue sharing the tokeniser with Mono's ASP.NET stack. It currently tokenises the whole HTML DOM as well as the ASP.NET controls and expressions, but this is not necessary for Mono's ASP.NET parser and only makes it hard to handle these corner cases.

GHOP Help Needed

The Google Highly Open Participation (GHOP) contest has been running for a couple of weeks now, and the Mono Project has seen a fair bit of action. Sadly we've only had five completed tasks, with a couple more almost done. On a lot of the claimed tasks, many of the students and mentors have been slow to act, busy with real life work. I think that the timescale of the tasks makes things difficult. It's not easy to think up a task that an inexperienced student can complete within five days, especially considering that they often need to get up to speed with the technologies before starting, and need to fit it in around schoolwork. Hopefully now that the Christmas holidays are beginning, students will have a lot more time to spend on the GHOP!

I apologise to any student who's been held up from starting further tasks because of delays in reviewing and closing the tasks. Doing a proper job takes quite a bit of time, and the Mono team has a lot of other important things to do too, like getting the shiny new Mono 1.2.6 and MonoDevelop 1.0 Beta 3 releases ready.

I'd like to put out a general request to everyone and anyone in the Mono community, or working with Mono-based apps, to lend a hand for the GHOP. You can submit proposals for tasks on the Mono GHOP issue tracker, and critique students' work. It doesn't matter if you can't write code — help with docs or icons. Go out and send students our way. This has the potential to be a huge community event. You can find us hanging out on #mono-ghop as well as the usual IRC channels on GIMPNet IRC.

MonoDevelop in the GHOP

So far, all five completed tasks for the Mono GHOP have been MonoDevelop -related. I'm really pleased with the amount of interest we've had so far. I'd like to thank our hard-working students and mentors, and the wonderful people at Google's Open-Source Program Office who put this contest together.

  • Dan Abramov submitted a very cleanly-written addin to integrate the Smokey code analysis tool . I'll be using this to tidy up minor bugs in the MonoDevelop codebase, and hopefully we can ship it in the addin repository after the 1.0 release.
  • This was followed by some fantastic icons from Vinicius Depizzol ("videpizzol"), and these will certainly be in the 1.0 release. They're not yet in SVN due to my pending review of our icon usage and handling in MonoDevelop.
  • The completed Brazilian Portuguese translation by Renato Felipe Atilio ("renatoat") is now our most up-to-date first translation, though due to changes to our translation infrastructure it's not merged into SVN yet.
  • The latest addition to our still-meagre documentation comes from "entereczek", a Tutorial for C/C++ Projects. I've put it on the MonoDevelop wiki.
  • Although not strictly a MonoDevelop contribution, "dwashing" created a MonoDevelop solution for F-Spot that's been committed to SVN.

Please continue to suggest, claim and review tasks. We still have plenty left on the issue tracker over at Google Code.Pop over to #mono-ghop on GIMPNet IRC to discuss things or to hang out with us.

Be careful using anonymous delegates across threads

[UPDATE]: It has been pointed out to me that the premise of this article is wrong. The compiler specifically avoids this problem by hoisting the captured variables into an inner class of which the generated method is also an instance member, and creating a new instance of this class for each call. I'm not sure what bug I ran into that causes the behaviour I denounced, or whether it was simply sloppy handling of reference types on my part -- but I feel particularly stupid having vaguely remembered "inner classes" from reading Raymond Chen's fine explanation of this over a year ago, yet not having checked it before posting this article. Nonetheless, I shall let the article remain as a reminder of the code that this wonderful feature saves you from having to write. Also, the queue/lock-based code may still be useful when one needs data passed to the GTK thread in a guaranteed order.

Anonymous delegates are incredibly useful, especially in that they can "capture" variables from a parent scope. Used within a single thread, they are very easy to understand. However, if you're using them to pass data across threads, you need to understand how the variable capture works.

Consider the following code snippet, in which the "data" variable is captured from the delegate's parent scope.

void ProcessIncomingData (string data)
{
	Gtk.Application.Invoke (delegate (object sender, EventArgs e) {
			textBuffer.AddText (data);
		});
}

What's wrong with this?

Let's expand the anonymous method into something resembling the compiler-generated code.

void ProcessIncomingData (string data)
{
	compilerHoistedVariable = name;
	Gtk.Application.Invoke (new EventHandler (compilerGeneratedMethod));
}
 
string compilerHoistedVariable = null;
 
void compilerGeneratedMethod (object sender, EventArgs e)
{
	textBuffer.AddText (compilerHoistedVariable);
}

Notice that the variable has been "hoisted" into the class scope, so that the generated method used for the anonymous delegate can access it. (The actual compiler-generated code is a bit different, encapsulating the hoisted variables in an inner class, but the principle is the same).

Now consider what happens if ProcessIncomingData gets called by the data-processing thread several times before the GTK+ loop gets run and can handle the invocations. Only the most recent value remains in the compiler-hoisted variable, and the earlier data strings are lost.

This isn't a problem if you're passing transient state, for example the visibility state of a button. But if you're passing data, think carefully about the internal mechanics. You may have to implement locking on the shared data structure.

void ProcessIncomingData (string data)
{
	lock (textBufferQueue) {
		textBufferQueue.Enqueue (data);
		if (!queueHandlerRunning) {
			queueHandlerRunning = true;
			Gtk.Application.Invoke (
				delegate (object sender, EventArgs e) {
					lock (textBufferQueue) {
						while (textBufferQueue.Count > 0) {
							textBuffer.AddText (textBufferQueue.Dequeue ());
						}
						queueHandlerRunning = false;
					}
				});
		}
	}
}
bool queueHandlerRunning = false;
Queue<string> textBufferQueue = new Queue<string> ();

This could hardly be described as "pretty", and the anonymous delegate has grown so big that we may as well move its definition out into a conventional method.

Things are easier if the delegate consumer offers an alternate call allowing you to pass arguments along with the delegate, such as Gtk.Application.Invoke and its Invoke (object sender, System.EventArgs args, System.EventHandler d) overload.

void ProcessIncomingData (string data)
{
	Gtk.Application.Invoke (
		this,
		new dataEventArgs (data),
		delegate (object sender, dataEventArgs e) {
			textBuffer.AddText (dataEventArgs.Data);
		});
}
 
class dataEventArgs : EventArgs
{
	public string Data;
	public dataEventArgs (string data)
	{
		this.Data = data;
	}
}

This is much more verbose than the method with which we started, but at least you won't lose any data and don't have to worry about locking.

Sadly, the anonymous types in C# 3.0 can't be used to simplify this. For a start, they don't allow inheritance. One might think that the "object sender" argument could be abused to pass an anonymous type, however in order to leave the scope of a method, the anonymous type must be cast to "object". The only way to access its properties would be via reflection, which would not only be verbose but would also carry a performance penalty.

Pages

Subscribe to Journal