It turns out that using C# was a good decision. I was a bit disappointed with Monodevelop because it appeared to lack debugging support and refactoring, plus it has some issues when encapsulating private fields as properties. The missing debugger turned out to be a missing plugin, which I installed. Still, it feels a bit unfinished (crashes, apparent lockups etc.)
Other than these minor nuisances, the language feels great. API exploration is much easier with autocompletion. Monodevelop's Stetic GUI designer helps a lot with building the GUI without remembering much about Gtk#/Gtk+. It's true it is also buggy (I had to edit its XML file by hand to remove some cruft it added), but it is a timesaver nevertheless.
I did make some progress with the Sexp parser - I am now capable of editing (slowly) little LISP programs. I do need to add keyboard shortcut support (all the 'structured' editing is menu driven right now), probably also through IAbstractEditor so the platform dependent stuff stays isolated.
No regrets over switching to C#. Looking forward to further development of Synpl (I'm thinking that once I get the basic parts right with the Sexp parser, I should add a more sophisticated parser - something like a parser for JavaScript/Pascal/Basic to check the usability of 'structured' editing further).
Monday, June 29, 2009
Sunday, June 14, 2009
Another Twist in the Road
This weekend I thought about using C# for Synpl. This was the original plan when I started working on the "structured editor" variation of Synpl, but I gave up because I didn't want to tie it to Windows.
Since wxWidgets is such a great library, I tried to find a .NET wrapper for it. There is one, but it's not updated since 2007, and it doesn't work out of the box. Maybe the fact that I tried it on 64bit Ubuntu didn't help, but I had to abandon it anyway.
So I tried Mono and Monodevelop and Gtk#. Gtk again. This time, I managed to tweak the GtkTextView and associated classes to do what I want (for instance, raise events similar to Scintilla's Modification event). There were some bugs, but I managed to find some quick workarounds (for instance, the C# Gtk.TextBuffer class doesn't have a CreateTag method :-) ).
I made some very quick progress and I like the result a lot. Besides, working with autocompletion is so much better than trying to figure out things in the plain Python REPL or the Gedit Python REPL. I guess autocompletion in the editor is a very good complement to a REPL sometimes.
So I'm back to Gtk (well, Gtk#, but only the language is different), but not Gedit since there is no Gedit-sharp.
It did feel like "lateral progress", but the development speed is better. And the final product will be faster.
I need to port the Python code to C# ... good thing there isn't so much of it. I wonder if I'll regret switching from Python to C#.
Wednesday, June 3, 2009
Editor components API
Today I managed to finish the tests for the "Text with changes" data structures described in a previous post.
I felt ready to create a basic Gedit plugin - but to my surprise, I found out that the API offered by Gedit doesn't offer a few essential notifications. I want to be able to register a callback with events such as 'buffer changes', regardless of cause (insertions, deletions, cuts, pastes, undos, redos etc.)
It turns out that the concepts used in the Gedit API are described in the PyGTK tutorial, but the event system in Gedit is even weaker than that in the TextView component of GTK (which doesn't offer generic enough 'buffer modification' events either).
I can hook up the keyboard events and handle insertions and deletions, but when it comes to cuts/pastes/undos/redos I have a hard time detecting the events (the user may use non-standard shortcuts) but, most importantly, I cannot isolate the text changes (which characters changed).
After a little more digging, I found out that 1) Scintilla supports a 'buffer modification' event that offers everything I need and 2) wxPython has a portable (GTK+/Win32) wrapper over Scintilla, along with demos with full source code that show how to use the control AND the 'buffer modification' notification/event.
So Synpl changes once again, not so radically this time. It was supposed to become a Gedit plugin, now it seems I'll end up with a standalone editor based on Scintilla and wxPython.
At least I feel like I'm making progress.
I felt ready to create a basic Gedit plugin - but to my surprise, I found out that the API offered by Gedit doesn't offer a few essential notifications. I want to be able to register a callback with events such as 'buffer changes', regardless of cause (insertions, deletions, cuts, pastes, undos, redos etc.)
It turns out that the concepts used in the Gedit API are described in the PyGTK tutorial, but the event system in Gedit is even weaker than that in the TextView component of GTK (which doesn't offer generic enough 'buffer modification' events either).
I can hook up the keyboard events and handle insertions and deletions, but when it comes to cuts/pastes/undos/redos I have a hard time detecting the events (the user may use non-standard shortcuts) but, most importantly, I cannot isolate the text changes (which characters changed).
After a little more digging, I found out that 1) Scintilla supports a 'buffer modification' event that offers everything I need and 2) wxPython has a portable (GTK+/Win32) wrapper over Scintilla, along with demos with full source code that show how to use the control AND the 'buffer modification' notification/event.
So Synpl changes once again, not so radically this time. It was supposed to become a Gedit plugin, now it seems I'll end up with a standalone editor based on Scintilla and wxPython.
At least I feel like I'm making progress.
Saturday, May 30, 2009
Synpl broken code uploaded on GitHub
Yes, I know, broken code is not very useful to other people. I just wanted to have some kind of backup :-)
Today I worked pretty hard at finding a way to store text changes between the last successful parse and the current moment. Turns out that storing information about white space and comments in the parsed information is not very useful. I've given up on moving comments along with the item they're referring to. For now.
The most interesting result of today is that a very neat way of storing the text changes is a kind of a diff from the last successful parse. This can also be stored on disk very efficiently (before that I was thinking about storing the parse tree representation, which is much larger).
The only file that works right now is TextWithChanges.py in the root of the repository. The unit tests show what it can do. I'm pretty happy with the abstraction (I'm certain I'm rediscovering something classic here, just not realizing it clearly yet).
Basically, a TextWithChanges (TWC) stores the characters that were added or deleted since the last successful (local) parse. It can provide the 'old' version (that parses) and the current version, using current coordinates (which are actual cursor positions from the editor).
An old version of a slice of a TWC can look quite weird, as deleted characters share the same position (in terms of actual cursor positions).
"An example looks like this."
The red characters were deleted, the green ones added. The old version for a slice will not include the green characters, the current version will not include the red ones. Once a slice parses successfully, the changes are forgotten (the characters become black again).
Storing the version with changes on disk allows the editor to have access to a previous valid parse. Since it's possible to store only the changes and refer the file content with a hash, this can be very space-effective.
Today I worked pretty hard at finding a way to store text changes between the last successful parse and the current moment. Turns out that storing information about white space and comments in the parsed information is not very useful. I've given up on moving comments along with the item they're referring to. For now.
The most interesting result of today is that a very neat way of storing the text changes is a kind of a diff from the last successful parse. This can also be stored on disk very efficiently (before that I was thinking about storing the parse tree representation, which is much larger).
The only file that works right now is TextWithChanges.py in the root of the repository. The unit tests show what it can do. I'm pretty happy with the abstraction (I'm certain I'm rediscovering something classic here, just not realizing it clearly yet).
Basically, a TextWithChanges (TWC) stores the characters that were added or deleted since the last successful (local) parse. It can provide the 'old' version (that parses) and the current version, using current coordinates (which are actual cursor positions from the editor).
An old version of a slice of a TWC can look quite weird, as deleted characters share the same position (in terms of actual cursor positions).
"An example looks like this."
The red characters were deleted, the green ones added. The old version for a slice will not include the green characters, the current version will not include the red ones. Once a slice parses successfully, the changes are forgotten (the characters become black again).
Storing the version with changes on disk allows the editor to have access to a previous valid parse. Since it's possible to store only the changes and refer the file content with a hash, this can be very space-effective.
Friday, May 29, 2009
Clipboard managers for Gnome
On Windows I use clipx to be more efficient when copying and pasting (no, it's not always code).
I'm playing again with Ubuntu and I miss clipx. Fortunately, there is parcellite which is a worthy replacement (Ubuntu packages).
With a bit of tweaking, I got it to behave almost like clipx. Copying and pasting is comfortable again!
I'm playing again with Ubuntu and I miss clipx. Fortunately, there is parcellite which is a worthy replacement (Ubuntu packages).
With a bit of tweaking, I got it to behave almost like clipx. Copying and pasting is comfortable again!
Wednesday, May 27, 2009
Sexp structural editing
Yesterday I managed to write some code should be very useful in creating a structured sexp editor plugin for Gedit. Need to clean it up a little bit, then post it to GitHub.
It's interesting how parsing for an editor needs to take into account detailed position information, whitespace and comments information (one of the tasks I want to accomplish is switching items in a sexp list without disturbing the existing layout - if possible - and move comments attached to an item along with the item). I previously thought that moving text chunks like that would require a very smart pretty printer, it turns out that may not be required.
However, there are still problems with languages with a more complicated syntax. I need to implement a parser for a simplified Basic to test the pretty printer-less approach in a more realistic setting.
Structured editing of sexp has long been accomplished in Emacs and lisp modes. I think it would be really interesting to see that extended to other languages, based on a lisp-like parse tree of the code.
It's interesting how parsing for an editor needs to take into account detailed position information, whitespace and comments information (one of the tasks I want to accomplish is switching items in a sexp list without disturbing the existing layout - if possible - and move comments attached to an item along with the item). I previously thought that moving text chunks like that would require a very smart pretty printer, it turns out that may not be required.
However, there are still problems with languages with a more complicated syntax. I need to implement a parser for a simplified Basic to test the pretty printer-less approach in a more realistic setting.
Structured editing of sexp has long been accomplished in Emacs and lisp modes. I think it would be really interesting to see that extended to other languages, based on a lisp-like parse tree of the code.
Useful trick for interactive Gedit Python API exploration
I mentioned the usefulness of 'dir' in trying to find out how to perform different tasks using the Gedit API. I should have thought about this earlier, but here is a dir+grep function that should help with the large number of methods associated with most Gedit API objects.
This is very neat - it was stupid of me to browse those long method name lists without some automated help, especially since it was so easy to implement.
>>> def dir2(o, str):
... return [k for k in dir(o) if k.find(str) != -1]
...
>>> v = window.get_active_view()
>>> dir2(v, "selection")
This is very neat - it was stupid of me to browse those long method name lists without some automated help, especially since it was so easy to implement.
Subscribe to:
Posts (Atom)