Building Console+, an enhanced (hopefully) interface to windows shells

Everyday, almost, I use three different shells, Command Prompt, PowerShell, and Bash for Git. I think the interface of those shells has lots to improve. As I am spoiled by good editors like Visual Studio, TextMate, Sublime, Notepad+, (but no Vim yet), I expected the similar level of maturity and convenience.

Console+ is on github now. Please bear in mind, it is code in progress.

So, I wanted to build an interface to them. I’m not building any shell. I’m going to build just an interface. And in this journey, I start learning a few tips with WPF. A geeky joy, as Kent Beck confesses in his book, “TDD By Example”.

Resources I used

As this is a WPF application with Win API calls, I don’t have enough knowledge or experience. I am more focust on Web development (thought I don’t like being labelled as mere web developer as some windows devs believe wrongly web development is child’s play)

So, I google a lot to understand how it can possibly work. This is the list of my resources.

Adding an icon to your application

I thought I would simply put Icon=”/Resource/Icon.ico”, but it wasn’t. You have to open the property dialog and set it there.

Handling RETURN and TAB on textbox

If “AcceptsReturn” and “AcceptsTab” are on, you can’t capture those key codes in the event. They are handled within the control, and the event is not escalated. So, turn them off.

Color coding in console

Colour is an attribute of the console, and you don’t get it from StandardOutput. You have to do something with windows api, and I’m not ready get my hands dirty with win api yet. I’ll depriortise this story 🙂

DispatcherTimer

Initially, I used System.Timers.Timer to update the screen regularly, but it didn’t work. Timer runs in a separate thread, and can’t update UI thread. You get “The calling thread cannot access this object …” error. In this case, DispatcherTimer is handy, as Tick event is fired in the dispatcher thread.

_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(1000);
_timer.Tick += (o, args) =>
					  {
						  tbxConsole.Text = _console.ReadAll();
						  MoveCursorToTheEnd();
					  };
_timer.IsEnabled = true;

Sending key event to the console

I need to send user’s key input to “cmd.exe” process. In WPF key event, I get KeyEventArgs, but I need to convert it to a character, and it is not possible with the help of win api.


public class KeyHelper
{
	public enum MapType : uint
	{
		MAPVK_VK_TO_VSC = 0x0,
		MAPVK_VSC_TO_VK = 0x1,
		MAPVK_VK_TO_CHAR = 0x2,
		MAPVK_VSC_TO_VK_EX = 0x3,
	}

	[DllImport("user32.dll")]
	public static extern int ToUnicode(
		uint wVirtKey,
		uint wScanCode,
		byte[] lpKeyState,
		[Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] 
		StringBuilder pwszBuff,
		int cchBuff,
		uint wFlags);

	[DllImport("user32.dll")]
	public static extern bool GetKeyboardState(byte[] lpKeyState);

	[DllImport("user32.dll")]
	public static extern uint MapVirtualKey(uint uCode, MapType uMapType);

	public static char GetCharFromKey(Key key)
	{
		char ch = ' ';

		int virtualKey = KeyInterop.VirtualKeyFromKey(key);
		byte[] keyboardState = new byte[256];
		GetKeyboardState(keyboardState);

		uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
		var stringBuilder = new StringBuilder(2);

		int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
		switch (result)
		{
			case -1:
				break;
			case 0:
				break;
			case 1:
				{
					ch = stringBuilder[0];
					break;
				}
			default:
				{
					ch = stringBuilder[0];
					break;
				}
		}
		return ch;
	}
}

Char.MinValue

Sometimes, not often, I want to return empty character in my method. String has string.empty, but until now, I ddin’t know that Char.MinValue exists. It’s really handly. Look at this code.

public char GetCharacterFrom(Key key)
{
    if (key == Key.LeftShift)
        return Char.MinValue;

    if (key == Key.Return)
        return (char) 13;

    return GetCharFromKey(key);
}

Avalon Text Editor

You can highlight the part of text with VisualLineElement. the Rendering article has more detailed description.

With StackOverflow’s avalonedit tag, you can read through useful tips

You can download the source code, a sample application, and help file from codeproject.

Changing text color in Avalon Text Editor

Changing color of text or highlighting text is quite tricky with Avalon Text Editor. Primarily it’s because Avalon is not RichTextEditor but code editor. Text are treated as string and you put meta data on those text if you want to change the format.

You need to create your own DocumentColorizingTransformer to highlight a part of your text, and then add it to your editor’s LineTransformers collection. I found an example of custom DocumentColorizingTransformer, bud spend some time to find out how to use it.

public void UpdateConsole()
{
	tbxConsole.Document.Text = _console.ReadAll();
	tbxConsole.ScrollToEnd();
	tbxConsole.TextArea.TextView.LineTransformers.Add(new ColorizeAvalonEdit());
}

public class ColorizeAvalonEdit : DocumentColorizingTransformer
{
	protected override void ColorizeLine(DocumentLine line)
	{
		if (line.Length == 0)
			return;

		int lineStartOffset = line.Offset;
		string text = CurrentContext.Document.GetText(line);
		int start = 0;
		int index;
		while ((index = text.IndexOf("Microsoft", start)) >= 0)
		{
			base.ChangeLinePart(
				lineStartOffset + index, // startOffset
				lineStartOffset + index + 10, // endOffset
				(VisualLineElement element) =>
				{
					// This lambda gets called once for every VisualLineElement
					// between the specified offsets.
					Typeface tf = element.TextRunProperties.Typeface;
					// Replace the typeface with a modified version of
					// the same typeface
					element.TextRunProperties.SetTypeface(new Typeface(
						tf.FontFamily,
						FontStyles.Italic,
						FontWeights.Bold,
						tf.Stretch
					));
				});
			start = index + 1; // search for next occurrence
		}
	}
}


to be continued…

Building Console+, an enhanced (hopefully) interface to windows shells