Tuesday, March 6, 2007

MonoDevelop Smart Indent

Since I joined the Mono team about 2 weeks ago, I've been working on a developer suite, MonoDevelop.

Anyways, for the past week or so I've been working on implementing "Smart Indent" for the editor to make my (and, likely, your) life easier when it comes to pumping out code. The less time I have to spending formatting my code, the more code I can write and the less frustrated and distracted I get having to fix the formatting. Net result? A happier programmer and more bang for the buck, which is what .NET is all about in the first place, right? ;)

So tonight, if you update your local MonoDevelop svn repository and build the latest revision, you'll be able to test out my first (of many?) gift to you, Smart Indent. May it put a smile on your face and make your dreams come true.

For those interested in how I did it, read on...

I started out taking a look at the old code. The way it worked was that effectively every time the programmer typed <Return>, it would take a peek at the previous line or two in order to get enough context to make a decision on how much to indent the code. Unfortunately, one or two lines is rarely ever enough context to really tell what is going on, so it was really difficult to get indenting right that way. I needed more context... way more context. Ideally, we'd also be able to readjust indenting on-the-fly as the user typed if he typed something that would change the indent level for that line (think: user types a lone '}' on a line to finish off a block).

To do this The Right Way(tm), I would need to keep a parse tree handy and keep it updated as the user typed, constantly checking state to see if the current line indent level needed to be altered. So that's effectively what I did... I wrote a state machine that kept a rough parse tree handy at all times (see Extras/CSharpBinding/FormattingStrategy/CSharpIndentEngine.cs for the code). It's somewhat crude, but it works (although I'm sure it will need some tweaking here and there). In order to keep the parse tree up-to-date, I hooked it up to the editor's KeyPress() event and that's where I did my tinkering with the indent level as well (after querying the CSharpIndentEngine state).

So there you have it...

6 comments:

Sandy said...

Dude! Thank you! This has been one of my pet peeves with MD for a *long* time now.

You and Lluis keep giving me new reasons to switch from JEdit + terminal. ;-)

Jeffrey Stedfast said...

Haha, yea, first thing I noticed when I got assigned to MonoDevelop was "wow, this Auto-Indent needs work" - at the time I didn't realize there was a "Smart Indent" option.

Miguel and I got to talking about what sort of improvements the editor needed, and he agreed this was one of them.

Later, I discovered there was a Smart Indent feature, so I enabled it and at first I thought, "Wow, ok, this is already done... scratch that off my list." But then I started writing more code and pretty quickly discovered it was really bad, tried to fix some of the logic in the code, but I quickly decided it needed a rewrite.

I'm sure there are at least a few issues with my new code (someone just mentioned it wasn't handling his code quite right on IRC) and I know of at least 1 bug myself (it doesn't handle "default :" - e.g. if there are spaces between "default" and the ":", but that should be a trivial fix tomorrow).

Rick said...

A long time ago I wanted to do a small vi emulation plugin for Monodevelop, but there was no independent plugin way to hook into the keypress event.

Do plugins now have access to the keypress event?

Daniel said...

Great, but now we should implement this in GtkSourceView. :-)

Jeffrey Stedfast said...

Rick, yea, you can implement a TextEditorExtension to hook into the KeyPress event.

Be aware that you will want to pass the event up the chain, though, in order for Undo/Redo support to work properly.

Jeffrey Stedfast said...

Daniel: absolutely. I'll be sending Paolo some code written in C to do it (I've already sent him a few revisions, but they are buggy) once I work out the last remaining known bugs in the logic. The code should be easily modified to fit c, c++ or java without too much work (mostly it'll be getting rid of support for verbatim string literals since only c# has those and maybe a few minor tweaks here or there).

Code Snippet Licensing

All code posted to this blog is licensed under the MIT/X11 license unless otherwise stated in the post itself.