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

MonoDevelop Tips: Workspace Layout

The MonoDevelop workspace consists of a central document surrounded by pads containing complementary information, tools and navigation aids. Pads can be accessed from the View->Pads and View->Debug Windows menus, and closed when they are not needed. They may be assigned keybindings, which will open the pad if necessary then bring keyboard focus to it. Pads may also be opened automatically by various commands, such as the "Find in Files" command, which opens a pad of search results.

Arranging Pads

You can drag pads around to arrange them however is most useful to your workflow. Pads can be docked on any side of the document editor, or adjacent to any other pad. If a pad is docked in the same position as another pad, tabs will be added to enable you to switch which of the two pads is visible.

You can even undock pads and move them to float beside MD or on another monitor. Pads that you use less frequently but still wish to be easily accessible can be "auto-hidden" using the "-" button at the top right of the pad. Auto-hidden pads are shown as a little indicator at the side of where the pad was previously docked, and when you hover over this indicator, the pad will be shown again. When the mouse and keyboard focus leaves it, it will hide again.

Layouts

Which pads are is useful is something that's generally dependent on the current context. For example when debugging, it is useful to have the debugger pads for viewing the stack, locals, etc. When using the visual designer, the toolbox and property grid pads are very important. For this reason, the state of the open pads is represented by a layout, and you can switch between layouts to suit your current needs.

Layouts are very simple. There is always one active layout, and any changes you make to the pads change only the active layout. The current active layout can be changed using the list in the View menu, or the Layouts combo box in the toolbar. A new layout can be created using View->New Layout.

There are several built-in layouts that MonoDevelop switches between automatically based on the current context. The Default layout is shown when MD opens, and when a single single is loaded. The Solution layout is activated while a solution is open. The Debug layout is activated while debugging. There is also a GUI Designer layout that can be activated while using the GTK# designer, but this is optional, and an be enabled in Preferences->Visual Style->GTK# Designer.

MonoDevelop Tips: Document Switcher

The document switcher is a quick way to switch focus between open documents and pads using the keyboard. It's activated by the Ctrl-tab or Ctrl-shift-tab combinations, and remains open as long as Control remains held down. While it's open, you can change the selection — up arrow or shift-tab moves the selection up, down arrow or tab moves the selection down, and the left and right arrows move the selection between the pads and documents lists. When you have selected the item you want, release Control, and it will be selected.

The document switcher

The documents list is sorted by which have been most recently used, and when the dialog is opened, the first document it selects is the item after the current active document, i.e. the document that was focussed before it, since it's assumed that you don't want to switch to the current document. However, this also make it very easy to switch between a few documents with minimal keystrokes.

MonoDevelop Tips: Suggestion Mode

The default mode of the code completion list is to complete the symbol that's being typed. Whenever the completion engine can determine that you are typing an existing symbol (such as a type name, variable name or member name), it automatically triggers the completion list and populates it with all the values that are valid at that point. As you type while the list is open, the list's selection updates to match what best fits what you're typing, and you can manually change the selection using the up/down arrow keys. When you press space, enter, tab, or any punctuation, the completion list "commits" the selection into the document, so you don't have to type the rest of the word manually. This is incredibly useful when you get used to it.

Sometimes the completion engine cannot provide a complete list of valid values, for example when you are defining a lambda at the point that you pass it to a method. In such cases, when you need to type a value that's not in the list, it would be very irritating for the list to commit its best match and overwrite what you're typing. Instead, the completion list goes into suggestion mode.

In suggestion mode, the selection highlight in the list is a rectangle around the selection, not a solid block. When the list is in suggestion mode, it will only commit on tab or enter, so you won't commit accidentally while typing a word. If you use arrow keys to change the selection, the list will go back into completion mode and the highlight will become solid.

The completion list in suggestion mode

Some users like to write code out of order, for example using symbols that don't yet exist, and then defining them symbols later, or writing code that does not parse correctly and fixing it up. Completion mode really makes that style of coding hard to do. The answer is a command that toggles the list into suggestion mode. You can access it via the Edit->Toggle Completion Suggestion Mode menu item, or the Alt-Shift-Space key binding. Once the list is toggled into suggestion mode, it will stay that was until you toggle it back. This it useful because you can switch back and forth as it suits you.

MonoDevelop Tips: Fullscreen Modes

Sometimes it's useful to be able to focus only on your code without the distractions of the pads and the rest of your desktop. MonoDevelop has two ways to make this easier.

The Maximized View can be toggled by double-clicking on the document tab, or using the context menu on the document tab and selecting Switch maximize/normal view. When in maximize view, all open pads are auto-hidden at the sides of the MonoDevelop windows, and all toolbars are hidden (everything in the toolbars is also accessible from the menus).

The Fullscreen View can be activated using the View->Fullscreen menu command. This makes the MonoDevelop window take up the entire screen, hiding the taskbar and the window border.

Both view modes can be used together to maximize the document area as much as possible.

MonoDevelop Tips: Import Type

One of my favourite features that we added to MonoDevelop 2.4 is the "import Type" command. It is accessed using the keybinding Ctrl-Alt-Space, and shows a list of all types in all namespaces in all referenced assemblies:

The completion list for the Import Type command

You can use our completion list filtering to find the type you're looking for, then, when you commit the selection from the list, MonoDevelop automatically adds the "using" statement to the file. For example, using StringBuilder is as easy as StrB even if you don't yet have using System.Text; at the top of the file.

MonoDevelop Tips: Completion List Filtering

MonoDevelop makes it really easy to search the code completion list. As you type, it breaks down the string you enter into word fragments on camelCase boundaries, then matches these fragments against the beginnings of the words in the completion list. The list is filtered to show only the items that match, and the matched parts are helpfully highlighted in blue:

Completion list filtering

This makes it much faster to select items from the list, since you can uniquely select items in the list without typing them out in full or using the arrow keys. For example, "StBu" will match "StringBuilder". It's also very useful for searching for items in the list if you're not sure what you need. For example "Str" will match both "StringComparison" and "ToString" and anything else with "Str" in its name.

If you use completion matching to filter the list then refine your selection with the arrow keys, when you commit an item from the list (using space, enter, tab, etc.), MonoDevelop will remember your choice. The next time you type the same string, it will automatically select the item you chose the previous time. However, if you type the exact name of something in the completion list, it will always match that item.

MonoDevelop Tips: Key Bindings

Key bindings are an important part of an IDE, as they make it possible to work efficiently using the keyboard. A key binding is a combination of keys that, when pressed simultaneously, activates an IDE command directly. This post explains the key binding system in MonoDevelop, how to find out what key bindings are available, and how to customize the bindings to suit your needs.

The Key Bindings Panel

To find what the key binding for a particular command is, you can look at the menu item for that command, or hover over the toolbar button. However, not all commands are exposed via menus and buttons. For a complete list, go to the Preferences dialog (MonoDevelop->Preferences on Mac, Tools->Options on Windows, Edit->Preferences on Linux), then the General->Key Bindings panel. This allows you to find existing key bindings, or modify the keybindings to your liking.

The key bindings panel in MonoDevelop

The panel has various parts:

Scheme selector
A key binding scheme is the entire set of key bindings in the IDE. There are various built-in schemes that can be chosen using the combo at the top of the panel. If you would like to contribute a new scheme, or have suggestions for improving one of the built-in schemes, please file a bug report. MonoDevelop currently does not support saving custom schemes, but you can find the scheme in MonoDevelop's preferences directory. On Mac it's ~/.config/MonoDevelop/KeyBindingsMac.xml, on Windows it's AppData\Roaming\MonoDevelop\KeyBindings.xml, and on Linux it's ~/.config/MonoDevelop/KeyBindings.xml.
Searchable list of bindings
The list of bindings can be searched to see what commands are available, and what key bindings they have. The search take into account the command's name, description and binding.
Conflict indicator
If multiple commands have the same keybinding, an indicator is shown with a drop-down list of the conflicting bindings. An indicator is also shown when the command is selected in the list.
Binding editor
The binding editor shows the currently selecting binding in the list. To edit the binding, simply place focus in the editor box and type the new keybinding. If you press multiple combinations in succession, it will show a "chord" of two bindings. Press Backspace to clear the binding. When you are happy with the new binding, press the Apply button.

Key Combinations and Chords

A key combination is a unique combination of a key and the keyboard modifiers that are active when the key is pressed - any combination of control, shift, alt/opt, super/windows/command. A key combination can be "bound" to a command, and this means that when the combination is pressed, then the command will be run. Keys without modifiers are valid "combinations" for binding too, which is useful for the function keys (F12 etc), arrow keys, the Page Up key, and so on. However it's a bad idea to bind keys that produce text/character input, since key bindings have top priority and therefore will prevent you from entering those characters.

It's not currently possible to bind more than one combination to each command, or more than one command to each unique combination. However, if you start to run out of key combinations, you can use chords. A keybinding chord is composed of two key combinations in succession. The first one, called a "mode" in emacs, can be shared by several chords, but this means it cannot be bound by itself, because that would conflict with the chords. For example, if I bound the command that shows the debugger locals pad to the "Ctrl-D Ctrl-L" chord, then I could not bind "Ctrl-D" by itself, but I could bind "Ctrl-L". I could create other keybindings using the same mode, such as "Ctrl-D Ctrl-S" for the debugger stack pad.

The Default Key Bindings

When we design the default key bindings for MonoDevelop, there are several important things we take into consideration.

  • It's essential to use the core operating system keybindings where applicable - cut, copy, paste, and so on.
  • It's good to have compatibility with other popular IDEs and text editors where possible.
  • It's important to use the more accessible keybindings for the common commands that are more likely to be used from the keyboard.
  • Unfortunately, not all editors and operating systems have the same keybindings, and they sometimes conflict with each other, so it's difficult to find a good balance.

MonoDevelop's default keybindings for Windows and Linux are the same, since GNOME and Windows have the same core keybindings. Additional bindings are mostly taken from various Visual Studio schemes, the GEdit text editor, Resharper, and SharpDevelop. MonoDevelop's default Mac keybindings are completely different, and mostly based on Xcode, with some from TextMate.

Building Mac Applications in MonoDevelop with MonoMac

Recently Miguel announced MonoMac, a new Mono binding for Cocoa & other Objective-C libraries, based on the MonoTouch binding generator and Objective-C bridge.

This is exciting for many people because it allows writing native Mac GUIs using C# and other .NET languages. I have put together a MonoDevelop addin that simplifies the process of creating, developing and debugging a MonoMac application.

I have created a brief walkthrough to show how easy it is to create a simple Mac application with MonoMac, MonoDevelop and Interface Builder.

First, create a new MonoMac C# project using the New Project dialog.

MonoDevelop New Project dialog showing MonoMac Project types

The new project has an entry point, a definition of the main menu and app delegate, and a window with a controller. The xib files are interface definitions that can be edited with Apple's Interface Builder GUI designer tool. The xib.designer.cs files contain autogenerated partial classes for any classes, actions and outlets defined in the xib files. The Info.plist file is an application manifest, and MonoDevelop will automatically merge in some required values when building the app bundle.

A newly-created MonoMac project in MonoDevelop

The MainWindow.xib file can be opened in Interface Builder.

Interface Builder with the MonoMac project's main window

After adding an NSButton and NSTextField to the window, add an outlet of type NSTextField to the controller definition in the Library.

Adding an outlet to the MainWindowController

Connect this outlet to the text field on the window by dragging the outlet from the Inspector.

Connecting the text field to the outlet on the controller

Similarly, create an action of type NSButton, and connect it to the button on the window.

Connecting the button to an action on the controller

MonoDevelop automatically updates the designer code when it regains focus. The designer partial class contains the new action and outlet.

The autogenerated designer code for the controller's outlets and actions

Implement the action in the controller class. MonoDevelop will automatically complete the partial method signature.

Implementation of the action in the controller

The application is compiled to an app bundle, which can be run or debugged like any other Mono application.

Debugging the MonoMac application

Note that this is an experimental preview, the result of a few days' work, and MonoMac itself is also in an immature state. I make no guarantee about stability of the MonoMac APIs, the MonoDevelop CodeBehind generator, the project format, or functionality of any of the usual MonoDevelop features. With these caveats, you can download a MonoDevelop build with MonoMac included.

UPDATE: The addin can now be installed into the latest version of MonoDevelop 2.4 by following these instructions.

This is a community effort, not a commercial product, so we need your help improving both MonoMac and the MonoDevelop addin!

  • Binding and auditing more Framework APIs.
  • Writing samples and templates to set the patterns for MonoMac applications.
  • Writing tutorials and documentation.
  • Porting Cocoa samples to exercise the bindings and tooling, and prioritize API binding.
  • Enabling the MonoDevelop addin to expose existing C# classes and project resources to Interface Builder.

Email the mono-osx mailing list to join in!

T4 Templates in MonoDevelop

A few week ago, I travelled to GDC and MIX. While in planes, airports and in spare moments in the conference, I implemented a feature I've wanted for some time - integrated T4 templating. This takes the T4 engine that I wrote for ASP.NET MVC templates, and exposed it within the IDE as a "custom tool", like Visual Studio does.

This also meant I had to implement a simple version of VS-style custom tools, or generators. Set the file's "custom tool" property to "TextTemplatingFileGenerator" using the property pad, then whenever you save it, MD will run the generator on the file.

Unlike VS, there is also a file template for the T4 file:
MonoDevelop New File dialog with T4 template

The T4 syntax is very like ASP.NET's render blocks, expressions and directives. Here you can see a simple example that generates a countdown:
T4 template in MonoDevelop generating a countdown

When this is saved, it generates the output file:
T4 output file in MonoDevelop

It's also integrated with MonoDevelop 2.4's new error bubbles, so any errors in T4 code show up in the editor as soon as the file is saved:
Error bubbles in T4 template in MonoDevelop

This is very much a side project for me, so contributions in this area would be very helpful. It would be great to have support for viewing generators' message output and cancelling long-running generators, support for T4 custom directives, debugging T4, code completion in T4 files, and generating template class files that can be compiled into apps.

This is part of the Catchup 2010 series of posts.

Iterator-based Microthreading

Back in May, I was wrapping PhyreEngine and porting the samples to C#. To extend one of them and demonstrate some of the capabilities of C#, Miguel and I decided to use simple iterator-based microthreading, which simulates multithreading but with many microthreads within a single real thread. Unity does something like it in their game engine too. It enables you to use a very imperative style of coding, as if using a single dedicated thread for each, but without anywhere near the overhead of real threads.

Here's the usage example we came up initially with that drove my design of the microthread scheduler:

public class Enemy1
{
	public void Init (Scheduler scheduler)
	{
		scheduler.AddTask (Patrol ());
	}
 
	IEnumerable Patrol ()
	{
		while (alive){
			if (CanSeeTarget ()) {
				yield return Attack ();
			} else if (InReloadStation){
				Signal signal = AnimateReload ();
				yield return signal;
			} else {
				MoveTowardsNextWayPoint ();
				yield return TimeSpan.FromSeconds (1);
			};
		}
		yield break;
	}
 
	IEnumerable Attack ()
	{
		while (TargetAlive && CanSeeTarget ()){
			AimAtTarget ();
			Fire ();
			yield return TimeSpan.FromSeconds (2);
		}
	}
}

The concept is fairly simple. The C# compiler magically transforms this code into an IEnumerator state machine (returned by IEnumerable.GetEnumerator()). Each time IEnumerator.MoveNext() is called, your method "iterates": it runs to the next yield statement, sets the Current property to the value yielded, and keeps enough state that next time it iterates, it can effectively resume where it left off. We can yield nulls to give other microthreads a chance to run, or yield objects to tell the scheduler other things. For example, yielding a TimeSpan could cause the microthread to sleep for that long.

interface IEnumerator
{
	bool MoveNext ();
	object Current;
}

As you can see, although C# iterators are primarily intended for iterating through collections, the yield keyword can also become effectively something like a microthread cooperatively yielding. Your method runs until it yields, then it later resumes from this point, runs to the next yield, and so on.

The class that enumerates your iterator is the scheduler. Before we get to that, we'll cover the boring bits to set some groundwork. First, we need a class to encapsulate the task. This holds hold the IEnumerator and which Scheduler it belongs to, is a singly linked list node, and has a field for arbitrary data we'll use later.

//tasks may move between lists but they may only be in one list at a time
internal class TaskItem
{
	public readonly IEnumerator Task;
	public TaskItem Next;
	public Scheduler Scheduler;
	public long Data;
 
	public TaskItem (IEnumerator task, Scheduler scheduler)
	{
		this.Task = task;
		this.Scheduler = scheduler;
	}
}

Next we need a simple linked list for keeping lists of TaskItem. We're not using LinkedList<T>; this is much simpler, does only what we need, and makes it easy to move tasks between lists and remove them via the enumerator.

internal sealed class TaskList
{
	public readonly Scheduler Scheduler;
 
	public TaskItem First { get; private set; }
	public TaskItem Last { get; private set; }
 
	public TaskList (Scheduler scheduler)
	{
		this.Scheduler = scheduler;
	}
 
	public void Append (TaskItem task)
	{
		Debug.Assert (task.Next == null);
		if (First == null) {
			Debug.Assert (Last == null);
			First = Last = task;
		} else {
			Debug.Assert (Last.Next == null);
			Last.Next = task;
			Last = task;
		}
	}
 
	public void Remove (TaskItem task, TaskItem previous)
	{
		if (previous == null) {
			Debug.Assert (task == First);
			First = task.Next;
		} else {
			Debug.Assert (previous.Next == task);
			previous.Next = task.Next;
		}
 
		if (task.Next == null) {
			Debug.Assert (Last == task);
			Last = previous;
		}
		task.Next = null;
	}
 
	public TaskEnumerator GetEnumerator ()
	{
		return new TaskEnumerator (this);
	}
 
	public sealed class TaskEnumerator
	{
		TaskList list;
		TaskItem current, previous;
 
		public TaskEnumerator (TaskList list)
		{
			this.list = list;
			previous = current = null;
		}
 
		public TaskItem Current { get { return current; } }
 
		public bool MoveNext ()
		{
			TaskItem next;
			if (current == null) {
				if (previous == null)
					next = list.First;
				else
					next = previous.Next;
			} else {
				next = current.Next;
			}
 
			if (next != null) {
				if (current != null)
					previous = Current;
				current = next;
				return true;
			}
			return false;
		}
 
		public void MoveCurrentToList (TaskList otherList)
		{
			otherList.Append (RemoveCurrent ());
		}
 
		public TaskItem RemoveCurrent ()
		{
			Debug.Assert (current != null);
			TaskItem ret = current;
			list.Remove (current, previous);
			current = null;
			return ret;
		}
	}
}

Now we can implement the scheduler. Using the scheduler is very simple. You register tasks with RegisterTask(IEnumerable), then call Run() to run all the active tasks for one yield iteration each. It handles sleeping and waking up tasks and removing tasks once they're finished.

public sealed class Scheduler
{
	TaskList active, sleeping;
 
	public Scheduler ()
	{
		active = new TaskList (this);
		sleeping = new TaskList (this);
	}
 
	public void AddTask (IEnumerator task)
	{
		active.Append (new TaskItem (task, this));
	}
 
	public void Run ()
	{
		//cache this, it's expensive to access DateTime.Now
		long nowTicks = DateTime.Now.Ticks;
 
		//move woken tasks back into the active list
		var en =  sleeping.GetEnumerator ();
		while (en.MoveNext ())
			if (en.Current.Data < nowTicks)
				en.MoveCurrentToList (active);
 
		//run all the active tasks
		en = active.GetEnumerator ();
		while (en.MoveNext ()) {
			//run each task's enumerator for one yield iteration
			IEnumerator t = en.Current.Task;
			if (!t.MoveNext ()) {
				//it finished, so remove it
				en.RemoveCurrent ();
				continue;
			}
 
			//check the current state
			object state = t.Current;
			if (state == null) {
				//it's just cooperatively yielding, state unchanged 
				continue;
			} else if  (state is TimeSpan) {
				//it wants to sleep, move to the sleeping list. we use the Data property for the wakeup time 
				en.Current.Data = nowTicks + ((TimeSpan)state).Ticks;
				en.MoveCurrentToList (sleeping);
			} else if (state is IEnumerable) {
				throw new NotImplementedException ("Nested tasks are not supported yet");
			} else {
				throw new InvalidOperationException ("Unknown task state returned:" + state.GetType ().FullName);
			} 
		}
	}
 
	internal void AddToActive (TaskItem task)
	{
		active.Append (task);
	}
}

At this point, it looks fairly useful, but let's add a simple synchronization primitive too. Microthreads should never make long blocking calls because they can't be pre-empted. Instead, we're going to let the microthread obtain and yield a signal object, which means it will not be scheduled until the signal has been set. Instead of using blocking APIs, you can use async APIs, create a signal object, wait on that, and have the callback set the signal. Or, thinking back to the initial game example, some game object's controlling microthread might want to sleep until another game object reaches a certain state; the target object can keep a signal accessible via a property that microthreads can read and wait on.

A thread can wait for more that one signal, and more than one thread can wait for a signal. Essentially we're going to have an equivalent of .NET's AutoResetEvent and WaitHandle.WaitAll(WaiHandle[]).

The signal's job is to keep a list of all the tasks that are waiting for it. When tasks start waiting, they move out of the scheduler's lists and are tracked by all the signals instead. Each signal increments/decrements the task's Data property to keep track of how many signals the task is waiting for. When the count reaches zero, the task can be moved back to the scheduler.

public class Signal
{
	static int nextId = int.MinValue;
 
	int id = nextId++;
	List<TaskItem> tasks = new List<TaskItem> ();
	bool isSet = true;
 
	public void Set ()
	{
		if (isSet)
			return;
		isSet = true;
		//decrement the wait count of all tasks waiting for thsi signal
		foreach (TaskItem task in tasks)
			if (--task.Data == 0)
				//if the wait count is zero, the task isn't waiting for any more signals, so re-schedule it
				task.Scheduler.AddToActive (task);
		tasks.Clear ();
	}
 
	internal void Add (TaskItem task)
	{
		//signal only becomes unset when it has tasks
		if (isSet)
			isSet = false;
		//the signal keeps a list of tasks that are waiting for it
		tasks.Add (task);
		//use the task's data for tracking the number of signals it's still waiting for
		task.Data++;
	}
}

And we need to add a couple more checks to the Scheduler.Run() state handling, so that when the task returns a signal or collection/array of signals, it's moved from the scheduler's lists to the tasks' lists.

} else if (state is Signal) {
	TaskItem task = en.RemoveCurrent ();
	task.Data = 0;
	((Signal)state).Add (task);
} else if (state is ICollection<Signal>) {
	TaskItem task = en.RemoveCurrent ();
	task.Data = 0;
	foreach (Signal s in ((ICollection<Signal>)state))
		s.Add (task);
}

And, there it is.

I have a few more ideas to improve this and turn it into a real library:

  • Implement a Signal that's a ManualResetEvent analogue
  • Use a IEnumerable<ThreadState> instead of plain IEnumerable, where ThreadState is a union struct with an enum specifying its type. This could be used to avoid the boxing of TimeSpan and the type checks and casts in the scheduler — just switch on the enum value. It would also prevent consumers returning a zero TimeSpan instead of null.
  • Dispose any disposable task IEnumerators
  • Implement task priorities, probably using different lists
  • Tidy up the accessibility and API a bit
  • Add a Scheduler.Run overload that only runs some number of iterations, not the whole list.

Some people may wonder why I haven't mentioned interactions with real threads. If the scheduler were thread-aware, then you could have multiple real threads on different cores consuming the microthread queue, and it would be faster with multiple cores and avoid blocking on slow tasks. The problem is not just that this increases the complexity, but that the microthreads all would have to be aware of threading too, and would need to lock all the objects they touched, and so on. This scheduler is meant to run in the equivalent of the GUI thread, purely to enable driving high-level game logic (and similar things) with an intuitive thread-like imperative programming model, and minimal overhead. If it doesn't fit easily on one core you're probably using it for the wrong thing. There might be cases where it's useful to create multiple Schedulers, and run them in different thread, but be careful about what the microthreads touch.

This is part of the Catchup 2010 series of posts.

Pages

Subscribe to Journal