<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6144304081336954203</id><updated>2011-07-08T06:15:13.974-07:00</updated><title type='text'>synpl</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4246092116323484773</id><published>2009-07-04T15:02:00.000-07:00</published><updated>2009-07-04T15:07:50.956-07:00</updated><title type='text'>Mono 2.4 Adventures</title><content type='html'>I ended up writing a shell script (mostly copy pasting from &lt;a href="http://www.centriment.com/2009/04/01/building-mono-24-from-source-on-ubuntu-810/"&gt;this blogpost containing building instructions&lt;/a&gt;) that wgets and compiles Mono 2.4 including MonoDevelop.&lt;br /&gt;&lt;br /&gt;I added it to the &lt;a href="http://github.com/mbrezu/synpl/tree/master"&gt;github repository&lt;/a&gt; (tools/get-and-build-mono.sh).&lt;br /&gt;&lt;br /&gt;Turns out that using checkinstall instead of the classic 'make install' is a big mistake, as it creates debs and exposes my shiny new Mono 2.4 to the whims of 'apt', which promptly overwrites it on the first update. I should probably learn more about the Debian package system and apt, but right now simply putting everything in /opt/mono-2.4 and recompiling when needed (the script hopefully can now run unattended) works well enough for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4246092116323484773?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4246092116323484773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4246092116323484773' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4246092116323484773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4246092116323484773'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/07/mono-24-adventures.html' title='Mono 2.4 Adventures'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-695574200963315869</id><published>2009-07-04T10:07:00.000-07:00</published><updated>2009-07-04T10:08:16.627-07:00</updated><title type='text'>Better way to install Mono 2.4</title><content type='html'>Found at &lt;a href="http://www.centriment.com/2009/04/01/building-mono-24-from-source-on-ubuntu-810/"&gt;this address&lt;/a&gt;. Advantages: installs alongside "official" Mono.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-695574200963315869?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/695574200963315869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=695574200963315869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/695574200963315869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/695574200963315869'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/07/better-way-to-install-mono-24.html' title='Better way to install Mono 2.4'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4693156795363009131</id><published>2009-07-02T10:27:00.001-07:00</published><updated>2009-07-02T12:27:54.007-07:00</updated><title type='text'>Building Mono and MonoDevelop from source on Ubuntu 9.04 64bit</title><content type='html'>Ok, I am officially fed up with the fact that the default MonoDevelop install on Ubuntu 9.04 isn't really able to do debugging.&lt;br /&gt;&lt;br /&gt;Following &lt;a href="http://blog.ruski.co.za/page/Install-Mono-on-Ubuntu.aspx"&gt;this blog&lt;/a&gt; I found information on how to build Mono from sources. Things worked, with one minor glitch - I tried using 'make -j2' to use both cores, but compilation failed. Using only one core worked fine.&lt;br /&gt;&lt;br /&gt;I also used checkinstall to make sure that 1) I can uninstall everything I compile cleanly and 2) I have .debs to reinstall later quickly if needed.&lt;br /&gt;&lt;br /&gt;Now I need to compile Gtk#. Prerequisites:&lt;br /&gt;&lt;br /&gt;sudo apt-get install libpango1.0-dev&lt;br /&gt;&lt;br /&gt;sudo apt-get install libatk1.0-dev&lt;br /&gt;&lt;br /&gt;sudo apt-get install libgtk2.0-dev&lt;br /&gt;&lt;br /&gt;sudo apt-get install libglade2-dev&lt;br /&gt;&lt;br /&gt;sudo apt-get install libgnomeui-dev libgnomecanvas2-dev libgnomeprint2.2-dev libgnomeprintui2.2-dev libpanel-applet2-dev libgnome2-dev&lt;br /&gt;&lt;br /&gt;Compile and install gtk# (from http://ftp.novell.com/pub/mono/sources/gtk-sharp212/gtk-sharp-2.12.8.tar.bz2 for instance).&lt;br /&gt;&lt;br /&gt;mono-addins (from http://ftp.novell.com/pub/mono/sources/mono-addins/mono-addins-0.4.tar.bz2)&lt;br /&gt;&lt;br /&gt;gnome-sharp (http://ftp.novell.com/pub/mono/sources/gnome-sharp220/gnome-sharp-2.20.1.tar.bz2)&lt;br /&gt;&lt;br /&gt;mono-tools (http://ftp.novell.com/pub/mono/sources/mono-tools/mono-tools-2.4.tar.bz2)&lt;br /&gt;&lt;br /&gt;sudo apt-get install libncurses5-dev&lt;br /&gt;&lt;br /&gt;mono-debugger (http://ftp.novell.com/pub/mono/sources/mono-debugger/mono-debugger-2.4.tar.bz2)&lt;br /&gt;&lt;br /&gt;monodevelop (http://ftp.novell.com/pub/mono/sources/monodevelop/monodevelop-2.0.tar.bz2)&lt;br /&gt;&lt;br /&gt;sudo apt-get install mozilla-dev (not sure this is actually required, it solves an error on my system).&lt;br /&gt;&lt;br /&gt;monodevelop-debugger-mdb (http://ftp.novell.com/pub/mono/sources/monodevelop-debugger-mdb/monodevelop-debugger-mdb-2.0.tar.bz2)&lt;br /&gt;&lt;br /&gt;nunit (from http://sourceforge.net/projects/nunit/files/NUnit%20Version%202/NUnit-2.4.8-&lt;br /&gt;src.zip?download)&lt;br /&gt;&lt;br /&gt;I ended up compiling nunit from source, using nunit-console.exe, nunit.core.dll and nunit.framework.dll as references in the test project and running nunit-console using something like:&lt;br /&gt;&lt;br /&gt;mono ./nunit-console.exe Synpl.Test.Core.dll&lt;br /&gt;&lt;br /&gt;to run my tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4693156795363009131?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4693156795363009131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4693156795363009131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4693156795363009131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4693156795363009131'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/07/building-mono-and-monodevelop-from.html' title='Building Mono and MonoDevelop from source on Ubuntu 9.04 64bit'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-6505341758849857024</id><published>2009-06-29T22:36:00.001-07:00</published><updated>2009-06-29T22:46:14.730-07:00</updated><title type='text'>Using C# and Mono</title><content type='html'>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.)&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-6505341758849857024?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/6505341758849857024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=6505341758849857024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/6505341758849857024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/6505341758849857024'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/06/using-c-and-mono.html' title='Using C# and Mono'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-7588746669604713208</id><published>2009-06-14T23:01:00.000-07:00</published><updated>2009-06-14T23:12:41.113-07:00</updated><title type='text'>Another Twist in the Road</title><content type='html'>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.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since wxWidgets is such a great library, I tried to find a .NET wrapper for it. There is &lt;a href="http://wxnet.sourceforge.net/"&gt;one&lt;/a&gt;, 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.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;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 :-) ).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;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.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So I'm back to Gtk (well, Gtk#, but only the language is different), but not Gedit since there is no Gedit-sharp.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It did feel like "lateral progress", but the development speed is better. And the final product will be faster.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;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#.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-7588746669604713208?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/7588746669604713208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=7588746669604713208' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/7588746669604713208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/7588746669604713208'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/06/another-twist-in-road.html' title='Another Twist in the Road'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-8183499263711130924</id><published>2009-06-03T11:14:00.000-07:00</published><updated>2009-06-03T11:24:36.319-07:00</updated><title type='text'>Editor components API</title><content type='html'>Today I managed to finish the tests for the "Text with changes" data structures described in a &lt;a href="http://synpl.blogspot.com/2009/05/synpl-broken-code-uploaded-on-github.html"&gt;previous post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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.)&lt;br /&gt;&lt;br /&gt;It turns out that the concepts used in the Gedit API are described in the &lt;a href="http://www.pygtk.org/pygtk2tutorial/index.html"&gt;PyGTK tutorial&lt;/a&gt;, 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).&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;At least I feel like I'm making progress.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-8183499263711130924?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/8183499263711130924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=8183499263711130924' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/8183499263711130924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/8183499263711130924'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/06/editor-components-api.html' title='Editor components API'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-5225312187818500127</id><published>2009-05-30T09:35:00.000-07:00</published><updated>2009-05-30T10:02:18.419-07:00</updated><title type='text'>Synpl broken code uploaded on GitHub</title><content type='html'>Yes, I know, broken code is not very useful to other people. I just wanted to have some kind of &lt;a href="http://github.com/mbrezu/synpl/tree/master"&gt;backup&lt;/a&gt; :-)&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;The only file that works right now is &lt;a href="http://github.com/mbrezu/synpl/blob/48c9988f90cb2808a0d6bc50dd772bbe2ab9fac0/TextWithChanges.py"&gt;TextWithChanges.py&lt;/a&gt; in the root of the &lt;a href="http://github.com/mbrezu/synpl/tree/master"&gt;repository&lt;/a&gt;. 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).&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;"An ex&lt;span style="color: rgb(204, 0, 0);"&gt;a&lt;/span&gt;mpl&lt;span style="color: rgb(204, 0, 0);"&gt;e&lt;/span&gt; lo&lt;span style="color: rgb(255, 0, 0);"&gt;o&lt;/span&gt;ks li&lt;span style="color: rgb(51, 204, 0);"&gt;ke this.&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-5225312187818500127?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/5225312187818500127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=5225312187818500127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5225312187818500127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5225312187818500127'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/synpl-broken-code-uploaded-on-github.html' title='Synpl broken code uploaded on GitHub'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-2947355772392956765</id><published>2009-05-29T22:05:00.000-07:00</published><updated>2009-05-29T22:08:56.916-07:00</updated><title type='text'>Clipboard managers for Gnome</title><content type='html'>On Windows I use &lt;a href="http://www.bluemars.org/clipx/"&gt;clipx&lt;/a&gt; to be more efficient when copying and pasting (no, it's not &lt;span style="font-style: italic;"&gt;always&lt;/span&gt; code).&lt;br /&gt;&lt;br /&gt;I'm playing again with Ubuntu and I miss clipx. Fortunately, there is &lt;a href="http://parcellite.sourceforge.net/"&gt;parcellite&lt;/a&gt; which is a worthy replacement (&lt;a href="https://launchpad.net/%7Eandrewsomething/+archive/ppa?field.name_filter=parcellite&amp;amp;field.status_filter=published"&gt;Ubuntu packages&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;With a bit of  tweaking, I got it to behave almost like clipx. Copying and pasting is comfortable again!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-2947355772392956765?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/2947355772392956765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=2947355772392956765' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/2947355772392956765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/2947355772392956765'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/clipboard-managers-for-gnome.html' title='Clipboard managers for Gnome'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-8126622690543625046</id><published>2009-05-27T22:58:00.000-07:00</published><updated>2009-05-27T23:07:52.404-07:00</updated><title type='text'>Sexp structural editing</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-8126622690543625046?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/8126622690543625046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=8126622690543625046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/8126622690543625046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/8126622690543625046'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/sexp-structural-editing.html' title='Sexp structural editing'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-1852997271632668515</id><published>2009-05-27T22:54:00.000-07:00</published><updated>2009-05-27T22:58:03.512-07:00</updated><title type='text'>Useful trick for interactive Gedit Python API exploration</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; def dir2(o, str):&lt;br /&gt;...  return [k for k in dir(o) if k.find(str) != -1]&lt;br /&gt;... &lt;br /&gt;&gt;&gt;&gt; v = window.get_active_view()&lt;br /&gt;&gt;&gt;&gt; dir2(v, "selection")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-1852997271632668515?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/1852997271632668515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=1852997271632668515' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1852997271632668515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1852997271632668515'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/useful-trick-for-interactive-gedit.html' title='Useful trick for interactive Gedit Python API exploration'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-631566231338535183</id><published>2009-05-25T12:38:00.000-07:00</published><updated>2009-05-25T12:48:49.555-07:00</updated><title type='text'>Final basic tasks for Gedit plugin API research</title><content type='html'>I need to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;be able to add menus and keyboard shortcuts&lt;/li&gt;&lt;li&gt;be able to use dialogs and auto-completion like drop-downs&lt;/li&gt;&lt;li&gt;write a EAL (editor abstraction layer) so the Python code I write can be ported with reasonable ease to other editors that support Python scripting/modules&lt;/li&gt;&lt;/ol&gt;For task number 1, I will look into:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://live.gnome.org/Gedit/PythonPluginHowTo#head-5d5e6827eac2ca14b5bd4d0fc7d88c28e646865c"&gt;http://live.gnome.org/Gedit/PythonPluginHowTo#head-5d5e6827eac2ca14b5bd4d0fc7d88c28e646865c&lt;/a&gt; (again)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.russellbeattie.com/blog/my-first-gedit-plugin"&gt;http://www.russellbeattie.com/blog/my-first-gedit-plugin&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;For task number 2:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://users.tkk.fi/%7Eotsaloma/gedit/completion.py"&gt;http://users.tkk.fi/~otsaloma/gedit/completion.py&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://linil.wordpress.com/2008/05/31/using-gedit-to-auto-complete-python-code/"&gt;http://linil.wordpress.com/2008/05/31/using-gedit-to-auto-complete-python-code/&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;Task number 3 needs more thought, hopefully the design will be somewhat clearer once tasks 1 and 2 are reasonably well researched and understood.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-631566231338535183?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/631566231338535183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=631566231338535183' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/631566231338535183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/631566231338535183'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/final-basic-tasks-for-gedit-plugin-api.html' title='Final basic tasks for Gedit plugin API research'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4662796678646150759</id><published>2009-05-25T12:05:00.000-07:00</published><updated>2009-05-25T12:12:37.600-07:00</updated><title type='text'>Gedit API - how to replace selected text</title><content type='html'>Today's post is rather short. It is also a bit incomplete, as I don't understand one of the functions I use (even though the others seem very simple and obvious).&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; v = window.get_active_view()&lt;br /&gt;&gt;&gt;&gt; b = v.get_buffer()&lt;br /&gt;&gt;&gt;&gt; b.insert_at_cursor("This is some sample text.\nHello, world!\n")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is basic code to populate the editing buffer with some text to experiment on.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; sel0 = b.get_iter_at_offset(8)&lt;br /&gt;&gt;&gt;&gt; sel1 = b.get_iter_at_offset(12)&lt;br /&gt;&gt;&gt;&gt; b.select_range(sel0, sel1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Selects the word "some" on the first line.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.delete_selection(0, 1)&lt;br /&gt;&gt;&gt;&gt; b.insert_at_cursor("emos")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first line deletes the selected text. I don't know the meaning of the parameters - I tried several combinations and they delete the entire selection or seem to have no effect. So I just use (0, 1) as parameters without understanding.&lt;br /&gt;&lt;br /&gt;The insert_at_cursor() call is already known and it inserts the string provided as parameter... at the current cursor position, obviously. Which is right where the selected text used to be, because we didn't move it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4662796678646150759?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4662796678646150759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4662796678646150759' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4662796678646150759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4662796678646150759'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/gedit-api-how-to-replace-selected-text.html' title='Gedit API - how to replace selected text'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-3076607661365647757</id><published>2009-05-20T22:30:00.001-07:00</published><updated>2009-05-20T23:24:18.507-07:00</updated><title type='text'>Gedit API Text Coordinates</title><content type='html'>I need to be able to convert between positions expressed as (line, column) pairs or (offset-in-characters-from-the-beginning-of-the-file). I also need to be able to get and set the cursor position and get and set the current selection. Also get the bounds of the document (first and last position).&lt;br /&gt;&lt;br /&gt;The content of this post (and its "sister posts" about the Gedit API) is in a "exploratory notes" format. It doesn't provide a full API description, it is just meant to help me rediscover quickly how to perform certain tasks. I expected I would get this information from some blog somewhere, but it doesn't seem to be the case. I'm making these notes public because I suspect someone may find them useful at some point. If you're that someone, don't forget to use dir(object) in the Python console to explore further, and to browse the C APIs for Gtk/Gdk and Gedit if you need to dig deeper. I used to have some experience with Gtk, but that is a long time ago and I used it from C, not Python. So I'm a kind of newbie to Gtk/Gedit programming. I never used the Gnome APIs ;-)&lt;br /&gt;&lt;br /&gt;Gedit uses the concepts of iterators and ranges to define positions in text and regions of text. An iterator can be used to inspect the text it points to, it can be moved forward and backward, it can be inspected to see if it is placed on the end of a line or word (this last feature is not that useful to me, as I'll have my own definitions for word/paragraph/block).&lt;br /&gt;&lt;br /&gt;How do we get an iterator? Easy:&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; v = window.get_active_view()&lt;br /&gt;&gt;&gt;&gt; b = v.get_buffer()&lt;br /&gt;&gt;&gt;&gt; b.insert_at_cursor("First line.\nSecond line\n.")&lt;br /&gt;&gt;&gt;&gt; start, end = b.get_bounds()&lt;br /&gt;&gt;&gt;&gt; end.get_line()&lt;br /&gt;2&lt;br /&gt;&gt;&gt;&gt; end.get_line_offset()&lt;br /&gt;0&lt;br /&gt;&gt;&gt;&gt; end.get_offset()&lt;br /&gt;25&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, get_bounds() returns a sequence containing the iterator that points to the beginning of the document and the end of the document (the first two lines are pretty self explanatory, a "buffer" is a "document", the model displayed in the view).&lt;br /&gt;&lt;br /&gt;"end" has a more interesting position ("start" is on offset 0, line 0, column 0, obviously). get_line() returns the line (counting from 0 as the first line), get_line_offset() returns the column (also counting from 0 as the first column) and get_offset() returns the number of characters since the beginning of the file (I suspect this will be the information I will use, as it is easily converted to line/column formats).&lt;br /&gt;&lt;br /&gt;Next: look at the text pointed by an iterator, move the iterator.&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; start.get_chars_in_line()&lt;br /&gt;12&lt;br /&gt;&gt;&gt;&gt; start.get_char()&lt;br /&gt;u'F'&lt;br /&gt;&gt;&gt;&gt; start.forward_char()&lt;br /&gt;True&lt;br /&gt;&gt;&gt;&gt; start.get_char()&lt;br /&gt;u'i'&lt;br /&gt;&gt;&gt;&gt; start.forward_to_line_end()&lt;br /&gt;True&lt;br /&gt;&gt;&gt;&gt; start.get_char()&lt;br /&gt;u'\n'&lt;br /&gt;&gt;&gt;&gt; start.get_offset()&lt;br /&gt;11&lt;br /&gt;&gt;&gt;&gt; start.get_line()&lt;br /&gt;0&lt;br /&gt;&gt;&gt;&gt; start.get_line_offset()&lt;br /&gt;11&lt;br /&gt;&gt;&gt;&gt; start.forward_char()&lt;br /&gt;True&lt;br /&gt;&gt;&gt;&gt; start.get_offset()&lt;br /&gt;12&lt;br /&gt;&gt;&gt;&gt; start.get_line()&lt;br /&gt;1&lt;br /&gt;&gt;&gt;&gt; start.get_line_offset()&lt;br /&gt;0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So I've looked at the char pointed to by the iterator, moved the iterator and played with its position to make sure my assumptions about get_line(), get_line_offset() and get_offset() are correct.&lt;br /&gt;&lt;br /&gt;It's a bit weird to use an iterator named "start" to do all this, but since iterators are mutable objects, it worked. BTW, we didn't affect the beginning of the file in any way. "start" just happened to begin its life by pointing at the beginning of the file, that's it.&lt;br /&gt;&lt;br /&gt;How to set the position of an iterator if we know the "offset", for instance?&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; start.get_offset()&lt;br /&gt;12&lt;br /&gt;&gt;&gt;&gt; end.set_offset(start.get_offset())&lt;br /&gt;&gt;&gt;&gt; end.get_offset()&lt;br /&gt;12&lt;br /&gt;&gt;&gt;&gt; end.get_char()&lt;br /&gt;u'S'&lt;br /&gt;&gt;&gt;&gt; start.get_char()&lt;br /&gt;u'S'&lt;br /&gt;&gt;&gt;&gt; end.get_line()&lt;br /&gt;1&lt;br /&gt;&gt;&gt;&gt; end.get_line_offset()&lt;br /&gt;0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So end and start point to the same position, and I can use an iterator to convert between the offset-from-BOF and (line, column) representations of a position. I can also set the line and column via set_line() and set_line_offset() then get the offset-from-BOF using get_offset(), so the conversion works both ways.&lt;br /&gt;&lt;br /&gt;How do I get the iterator at cursor? Place the cursor at the end of the word "Second" on the second line of text using the mouse.&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.get_insert()&lt;br /&gt;&lt;gtk.TextMark object at 0x2f1a230 (GtkTextMark at 0x2f54320)&gt;&lt;br /&gt;&gt;&gt;&gt; b.get_iter_at_mark(b.get_insert())&lt;br /&gt;&lt;GtkTextIter at 0x7fe424001e80&gt;&lt;br /&gt;&gt;&gt;&gt; at_cursor = b.get_iter_at_mark(b.get_insert())&lt;br /&gt;&gt;&gt;&gt; at_cursor.get_line()&lt;br /&gt;1&lt;br /&gt;&gt;&gt;&gt; at_cursor.get_line_offset()&lt;br /&gt;6&lt;br /&gt;&gt;&gt;&gt; at_cursor.get_char()&lt;br /&gt;u' '&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I don't know what a mark is yet, and I don't know if these iterators and marks don't need to be removed somehow to avoid memory leaks. However, I'm not too worried about that now. If it becomes a problem, I'll fix it.&lt;br /&gt;&lt;br /&gt;The "at_cursor" iterator seems to be positioned properly, though.&lt;br /&gt;&lt;br /&gt;How to position the cursor using an iterator (the reverse of getting the position of the cursor as an iterator)? I'll just try to move the cursor over the space between "Second" and "line", then get the position of the cursor again and checked it moved.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; at_cursor.forward_char()&lt;br /&gt;True&lt;br /&gt;&gt;&gt;&gt; b.place_cursor(at_cursor)&lt;br /&gt;&gt;&gt;&gt; at_cursor_new = b.get_iter_at_mark(b.get_insert())&lt;br /&gt;&gt;&gt;&gt; at_cursor_new.get_offset()&lt;br /&gt;19&lt;br /&gt;&gt;&gt;&gt; at_cursor.get_offset()&lt;br /&gt;19&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the relevant call is "b.place_cursor()".&lt;br /&gt;&lt;br /&gt;What if I want to find out the coordinates of the current selection? Make sure no text is selected.&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.get_selection_bounds()&lt;br /&gt;()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now select "ond" in "Second" on the second line.&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.get_selection_bounds()&lt;br /&gt;(&lt;GtkTextIter at 0x31548a0&gt;, &lt;GtkTextIter at 0x315a1e0&gt;)&lt;br /&gt;&gt;&gt;&gt; sel0, sel1 = b.get_selection_bounds()&lt;br /&gt;&gt;&gt;&gt; sel0.get_offset()&lt;br /&gt;15&lt;br /&gt;&gt;&gt;&gt; sel1.get_offset()&lt;br /&gt;18&lt;br /&gt;&gt;&gt;&gt; sel1.get_char()&lt;br /&gt;u' '&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So "get_selection_bounds()" returns two iterators, one for the begining and the other for the end of the selection. "sel1" points to the first character after the selection.&lt;br /&gt;&lt;br /&gt;How do I set the selection? Deselect the text.&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.select_range(sel0, sel1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The text is selected again.&lt;br /&gt;&lt;br /&gt;How do I get the text between two iterators (using a loop and get_char() seems wasteful, especially since Python strings are immutable)?&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; b.get_text(sel0, sel1)&lt;br /&gt;'ond'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That concludes the things I wanted to explore in this post. Again, these are just a newbie's personal notes and I will use them to draft a plugin. There are probably better ways to accomplish the tasks described in this post. I was just happy to find one way to perform them, given the lack of documentation (this perception of the documentation is very subjective, some APIs have far worse documentation, some far better; however, since I use the Python "dir()" function and a lot of trial and error, I dare say "lack of documentation").&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-3076607661365647757?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/3076607661365647757/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=3076607661365647757' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/3076607661365647757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/3076607661365647757'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/gedit-api-text-coordinates.html' title='Gedit API Text Coordinates'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-1705338779629524592</id><published>2009-05-18T22:16:00.001-07:00</published><updated>2009-05-18T22:33:41.615-07:00</updated><title type='text'>Test for the syntax highlighter</title><content type='html'>The point of this post is to test the syntax highlighter.&lt;br /&gt;&lt;pre name="code" id="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; a = ['Some', 'test']&lt;br /&gt;&gt;&gt;&gt; ', '.join(a)&lt;br /&gt;'Some, test'&lt;br /&gt;&lt;/pre&gt;Seems I managed to convince it to work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-1705338779629524592?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/1705338779629524592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=1705338779629524592' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1705338779629524592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1705338779629524592'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/test-for-syntax-highlighter.html' title='Test for the syntax highlighter'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-5489216308828129724</id><published>2009-05-18T13:24:00.000-07:00</published><updated>2009-05-18T22:35:56.650-07:00</updated><title type='text'>Gedit colorization</title><content type='html'>So, here's a gedit text colorization snippet.&lt;br /&gt;&lt;br /&gt;Start with an empty document and open the bottom pane, with the Python console activated (View|Bottom Pane, Edit|Preferences|Plugins).&lt;br /&gt;&lt;br /&gt;Enter this code:&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; v = window.get_active_view()&lt;br /&gt;&gt;&gt;&gt; b = v.get_buffer()&lt;br /&gt;&gt;&gt;&gt; t = b.create_tag("simple_tag")&lt;br /&gt;&gt;&gt;&gt; b.insert(b.get_start_iter(), "hello")&lt;br /&gt;&gt;&gt;&gt; b.apply_tag(t, b.get_start_iter(), b.get_end_iter())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Nothing happens, we need to change the colors associated with the tag. It looks like I need to install some kind of support for inserting source code in blog posts. Tomorrow I will try &lt;a href="http://pleasemakeanote.blogspot.com/2008/06/posting-source-code-in-blogger.html"&gt;these suggestions&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; import gtk&lt;br /&gt;&gt;&gt;&gt; t.props.foreground_gdk = gtk.gdk.Color("#fa0")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Tada! The text is now colorized. Since I'm selfish and sleepy, I won't go into details about the functions that were used (this post is mainly a self-note of how to perform text colorization). If anyone reads this and you feel like you could use some advice, try:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="Python"&gt;&lt;br /&gt;&gt;&gt;&gt; dir(b)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;'dir'ring python objects is helpful at times. Still, it would be better if the gedit/gtksourceview guys provided more documentation.&lt;br /&gt;&lt;br /&gt;Good night to myself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-5489216308828129724?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/5489216308828129724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=5489216308828129724' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5489216308828129724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5489216308828129724'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/gedit-colorization.html' title='Gedit colorization'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-293880276904341065</id><published>2009-05-18T13:10:00.000-07:00</published><updated>2009-05-18T13:24:21.066-07:00</updated><title type='text'>Resurrection... again</title><content type='html'>So I tried working on this project last year... and I I failed. Or at list I didn't get what I wanted, just a better understanding of monads and with that the conclusion that I don't like working with them.&lt;br /&gt;&lt;br /&gt;It seems the old synpl project is like a fungus, it never dies if left alone in dark corners with high humidity.&lt;br /&gt;&lt;br /&gt;I'm going to try again. This time, no super PL/SQL editor, no C compiler... but a structured editor.&lt;br /&gt;&lt;br /&gt;Why? I'm very impressed with the usability of org-mode in Emacs and I want that kind of functionality (moving around content as if it was a tree) in other editing tasks.&lt;br /&gt;&lt;br /&gt;I guess this experiment will die an unspectacular death like the previous ones. Well, if that's what it takes to keep the project alive...&lt;br /&gt;&lt;br /&gt;Enough rambling. Time to actually use this blog space for something practical (and maybe useful to others).&lt;br /&gt;&lt;br /&gt;Since I am not completely crazy, I don't want to implement an editor from scratch. I also don't want to use emacs (somehow elisp seems hard to use for large projects). I will use... gedit.&lt;br /&gt;&lt;br /&gt;Yes, the puny notepad like editor distributed with Gnome.&lt;br /&gt;&lt;br /&gt;Actually, it turns out that it is a very nice editor. For my purposes, at least. Because it has all the features I want (stylable editor, an API - in Python and explorable with a REPL, no less).&lt;br /&gt;&lt;br /&gt;So my learning tasks are now these:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Learn the Gedit API.&lt;/li&gt;&lt;li&gt;Write an editor wrapper lib for the functionality I need (so I can switch to IronPython and ScintillaNET if I want to port my little scripts to Windows).&lt;/li&gt;&lt;li&gt;Write a Scheme/Lisp parser (or use an existing one) and test the usability of the structured approach.&lt;/li&gt;&lt;li&gt;Keep going with a JavaScript parser and structured editor.&lt;/li&gt;&lt;li&gt;Extend the JavaScript parser to support QooxDoo and project files (discovered automatically from exploring the file system neighborhood of the current file).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;... hmmm, profit? (mentally, at least)&lt;/li&gt;&lt;/ol&gt;Step number 1 is going to take a few blogposts. I intend to write small tutorials on programming  the building blocks of my structured editor with the Gedit API. A preliminary list of these is:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Get the cursor position.&lt;/li&gt;&lt;li&gt;Set the cursor position.&lt;/li&gt;&lt;li&gt;Get the current selection.&lt;/li&gt;&lt;li&gt;Set the current selection.&lt;/li&gt;&lt;li&gt;Change text.&lt;/li&gt;&lt;li&gt;Colorize text.&lt;/li&gt;&lt;li&gt;Handle keyboard shortcuts.&lt;/li&gt;&lt;/ol&gt;Now that I know what I'll do with my free time, it's time to go to bed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-293880276904341065?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/293880276904341065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=293880276904341065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/293880276904341065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/293880276904341065'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2009/05/resurrection-again.html' title='Resurrection... again'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-7557477651831167723</id><published>2008-07-16T13:17:00.000-07:00</published><updated>2008-07-16T13:32:31.743-07:00</updated><title type='text'>Resurrection attempt...</title><content type='html'>Hmmm... It looks like I've abandoned this project for almost one year.&lt;br /&gt;&lt;br /&gt;Since I'm trying to learn Haskell and functional languages are supposed to be really good at handling compiling/translating/transforming tasks, it may be a good idea to try to reimplement things in Haskell. This and reading '&lt;a href="http://book.realworldhaskell.org/"&gt;Real world Haskell&lt;/a&gt;' should complement nicely.&lt;br /&gt;&lt;br /&gt;Here's a first attempt: a simple lexer written in less than 100 lines of Haskell (though it's pretty 'high density', without any comments).&lt;br /&gt;&lt;br /&gt;A few tasks that should help me become a better Haskell programmer:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;document things (the literate Haskell mode works great with Emacs, no excuses this time); add examples of usage in the comments !&lt;br /&gt;&lt;/li&gt;&lt;li&gt;learn how to isolate the lexer guts by using the Haskell module system&lt;/li&gt;&lt;li&gt;use the State monad, because there is state passed around in the lexer (although in a rather uniform manner, which avoids 'ladders to the right of the screen')&lt;/li&gt;&lt;li&gt;maybe use the Reader monad (there are some configuration items, such as the width of a tab)&lt;/li&gt;&lt;li&gt;... so maybe learn how to use monad transformers to 'stack' State and Reader?&lt;/li&gt;&lt;/ul&gt;Another change is that I decided that synpl needs to be open source this time. No foolish dreams of easy money :-) Anyway, since there is no way to submit archives to blogger, I should make a SourceForge or Google code project for this. Or, given that there are so many Haskell 'bosses' involved with Microsoft, maybe CodePlex...&lt;br /&gt;&lt;br /&gt;Until then, here's my newbie Haskeller code (uncommented :-( ):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&gt; data StreamPosition = StreamPosition { line :: Int, column :: Int }&lt;br /&gt;&gt;                       deriving Show&lt;br /&gt;&lt;br /&gt;&gt; data TokenPosition = TokenPosition { start :: StreamPosition,&lt;br /&gt;&gt;                                      end :: StreamPosition }&lt;br /&gt;&gt;                      deriving Show&lt;br /&gt;&lt;br /&gt;&gt; data TokenContent = TokenContent { position :: TokenPosition,&lt;br /&gt;&gt;                                    content :: String }&lt;br /&gt;&gt;                     deriving Show&lt;br /&gt;&lt;br /&gt;&gt; data Token = TokenNumber TokenContent&lt;br /&gt;&gt;            | TokenIdentifier TokenContent&lt;br /&gt;&gt;            | TokenChar TokenContent&lt;br /&gt;&gt;              deriving Show&lt;br /&gt;&gt; data LexerState = LexerState StreamPosition String [Token]&lt;br /&gt;&lt;br /&gt;&gt; lex :: String -&gt; [Token]&lt;br /&gt;&gt; lex input = lexImpl $ LexerState (StreamPosition 1 1) input []&lt;br /&gt;&gt;     where lexImpl (LexerState pos [] tokens) = reverse tokens&lt;br /&gt;&gt;           lexImpl arg@(LexerState pos input@(c:cs) tokens)&lt;br /&gt;&gt;                     | c == '\n' = lexImpl $ LexerState (addToLine pos 1) cs tokens&lt;br /&gt;&gt;                     | c == ' ' = lexImpl $ LexerState (addToColumn pos 1) cs tokens&lt;br /&gt;&gt;                     | c == '\n' = lexImpl $ LexerState (addToColumn pos 8) cs tokens&lt;br /&gt;&gt;                     | isDigit c = lexImpl $ lexNumber arg&lt;br /&gt;&gt;                     | isLetterOrUnderscore c = lexImpl $ lexIdentifier arg&lt;br /&gt;&gt;                     | otherwise = lexImpl $ LexerState (addToColumn pos 1) cs upTokens&lt;br /&gt;&gt;                                 where upTokens = (charTok : tokens)&lt;br /&gt;&gt;                                       tokenPos = TokenPosition pos pos&lt;br /&gt;&gt;                                       charTok = TokenChar $ TokenContent tokenPos [c]&lt;br /&gt;&gt;           isDigit c = c &gt;= '0' &amp;amp;&amp;amp; c &lt;= '9' &lt;br /&gt;&gt;           isLetterOrUnderscore c = c == '_' || isLower || isUpper&lt;br /&gt;&gt;               where isLower = c &gt;= 'a' &amp;amp;&amp;amp; c &lt;= 'z' &lt;br /&gt;&gt;                     isUpper = c &gt;= 'A' &amp;amp;&amp;amp; c &lt;= 'Z' &lt;br /&gt;&gt;           lexNumber :: LexerState -&gt; LexerState&lt;br /&gt;&gt;           lexNumber = lexEntity isDigit TokenNumber&lt;br /&gt;&gt;           lexIdentifier = lexEntity isLetterOrUnderscore TokenIdentifier&lt;br /&gt;&gt;           lexEntity :: (Char -&gt; Bool) -&gt; (TokenContent -&gt; Token) -&gt; LexerState -&gt; LexerState&lt;br /&gt;&gt;           lexEntity spanP tokenCtor (LexerState pos cs tokens) = LexerState upPos upCs upTokens&lt;br /&gt;&gt;               where (content, upCs) = span spanP cs&lt;br /&gt;&gt;                     upPos = addToColumn pos (length content)&lt;br /&gt;&gt;                     tokenPos = TokenPosition pos (addToColumn upPos (-1))&lt;br /&gt;&gt;                     numTok = tokenCtor (TokenContent tokenPos content)&lt;br /&gt;&gt;                     upTokens = (numTok : tokens)&lt;br /&gt;&gt;           addToLine (StreamPosition line column) lineInc = StreamPosition (line + lineInc) column&lt;br /&gt;&gt;           addToColumn (StreamPosition line column) columnInc =&lt;br /&gt;&gt;               StreamPosition line (column + columnInc)&lt;br /&gt;&lt;br /&gt;&gt; data TokenTransform = TokenTransform { predicate :: ([Token] -&gt; Int),&lt;br /&gt;&gt;                                        combiner :: ([Token] -&gt; [Token]) }&lt;br /&gt;&lt;br /&gt;&gt; applyTokenTransform :: [Token] -&gt; TokenTransform -&gt; [Token]&lt;br /&gt;&gt; applyTokenTransform [] _ = []&lt;br /&gt;&gt; applyTokenTransform tokens transform@(TokenTransform p c) =&lt;br /&gt;&gt;     let matchLen = p tokens&lt;br /&gt;&gt;     in if matchLen &gt; 0&lt;br /&gt;&gt;        then let (theMatch, theRest) = splitAt matchLen tokens&lt;br /&gt;&gt;                 theRest' = applyTokenTransform theRest transform&lt;br /&gt;&gt;             in (c theMatch) ++ theRest'&lt;br /&gt;&gt;        else let theHead = head tokens&lt;br /&gt;&gt;                 theRest = tail tokens&lt;br /&gt;&gt;             in (theHead : applyTokenTransform theRest transform)&lt;br /&gt;&lt;br /&gt;&gt; applyTokenTransforms :: [Token] -&gt; [TokenTransform] -&gt; [Token]&lt;br /&gt;&gt; applyTokenTransforms tokens (t:ts) = applyTokenTransforms (applyTokenTransform tokens t) ts&lt;br /&gt;&gt; applyTokenTransforms tokens [] = tokens&lt;br /&gt;&lt;br /&gt;&gt; matchPair :: Char -&gt; Char -&gt; TokenTransform&lt;br /&gt;&gt; matchPair char1 char2 = TokenTransform pred combiner&lt;br /&gt;&gt;     where isChar (TokenChar tc) ch = content tc == [ch]&lt;br /&gt;&gt;           isChar _ _ = False&lt;br /&gt;&gt;           pred [] = 0&lt;br /&gt;&gt;           pred [x] = 0&lt;br /&gt;&gt;           pred (el1 : el2 : els) | isChar el1 char1 &amp;amp;&amp;amp; isChar el2 char2 = 2&lt;br /&gt;&gt;                                  | otherwise = 0&lt;br /&gt;&gt;           combiner (el1 : el2 : els) = (combineTokenChars el1 el2 : els)&lt;br /&gt;&gt;               where combineTokenChars (TokenChar tc1) (TokenChar tc2) =&lt;br /&gt;&gt;                         let startPos = (start . position)&lt;br /&gt;&gt;                             endPos = (end . position)&lt;br /&gt;&gt;                             newTokenPosition = TokenPosition (startPos tc1) (endPos tc2)&lt;br /&gt;&gt;                         in TokenChar $ TokenContent newTokenPosition (content tc1 ++ content tc2)&lt;br /&gt;&lt;br /&gt;&gt; matchOps :: [TokenTransform]&lt;br /&gt;&gt; matchOps = [matchPair '=' '=', matchPair '&gt;' '=', matchPair '&lt;' '=']  &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-7557477651831167723?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/7557477651831167723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=7557477651831167723' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/7557477651831167723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/7557477651831167723'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2008/07/resurrection-attempt.html' title='Resurrection attempt...'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4018664314506887334</id><published>2007-08-31T00:21:00.000-07:00</published><updated>2007-08-31T01:24:20.285-07:00</updated><title type='text'>Pretty printing comments</title><content type='html'>(Note: there's a &lt;a href="http://www.snapdrive.net/files/483953/synpl-2007-08-31.zip"&gt;new demo&lt;/a&gt; implementing what's described below; sadly, &lt;a href="http://snapdrive.net/"&gt;snapdrive.net&lt;/a&gt; seems to have a lot of downtime, if the link doesn't work please try later)&lt;br /&gt;&lt;br /&gt;Pretty printing is simple, right? Just render the syntax tree as text in a pretty way and that's it.&lt;br /&gt;&lt;br /&gt;Hmmm. What about comments? Some pretty printers (Synpl earlier versions included) just throw away comments, since they don't appear in the syntax tree. This is &lt;span style="font-style: italic;"&gt;very &lt;/span&gt;wrong, as it loses precious information.&lt;br /&gt;&lt;br /&gt;If we pretty print comments, where do we place them? The shape of the program may have changed drastically by reformatting it.&lt;br /&gt;&lt;br /&gt;The solution I adopted is pretty naive, but it works rather well. This is what I do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;when tokenizing the source, collect comments in a separate list; record the content and the starting and ending positions for each comment;&lt;/li&gt;&lt;li&gt;allow some syntax tree nodes to have "after" and "before" comments; a good list of nodes to allow comments is: statements, item declarations in "DECLARE" sections of blocks, functions, procedures, triggers, packages (both spec and body) and types (also both spec and body);&lt;/li&gt;&lt;li&gt;associate each comment with the closest node that accepts comments and doesn't overlap the comment;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;when pretty printing the node, print the comments with the same level of indentation as the node (for both "above" and "before" list of comments);&lt;/li&gt;&lt;/ul&gt;This works well, but... what about cases like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;begin&lt;br /&gt;if 1 &amp;lt; a then&lt;br /&gt; c := 1;&lt;br /&gt;else&lt;br /&gt; null;&lt;br /&gt; -- uncomment next PL/SQL line to remove warnings about variable c&lt;br /&gt; -- sometimes being used before initialization&lt;br /&gt;&lt;br /&gt; --c := 2;&lt;br /&gt;end if;&lt;br /&gt;null;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;There are three comments here. They should all be associated to the &lt;span style="font-family:courier new;"&gt;null&lt;/span&gt; statement after the else. This is what happens for the first two comments. But the third is "closer" to the second &lt;span style="font-family:courier new;"&gt;null &lt;/span&gt;statement, and is therefore inserted in its "before" list and shows between &lt;span style="font-family:courier new;"&gt;end if&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;null &lt;/span&gt;in the pretty print. This is clearly wrong. Also, the blank line between comments is lost in translation.&lt;br /&gt;&lt;br /&gt;The solution? Group consecutive comments together into a larger comment, and preserve the blank lines in between.&lt;br /&gt;&lt;br /&gt;After implementing comment grouping things work as expected. There are plenty of things left to chance, such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;what to do with code inside comments&lt;/li&gt;&lt;li&gt;what about &lt;span style="font-style: italic;"&gt;broken &lt;/span&gt;code inside comments&lt;/li&gt;&lt;li&gt;what about formatting paragraphs in comments so they fit in the required number of columns?&lt;/li&gt;&lt;/ul&gt;Turns out pretty printing is an art in itself :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4018664314506887334?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4018664314506887334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4018664314506887334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4018664314506887334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4018664314506887334'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/08/pretty-printing-comments.html' title='Pretty printing comments'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-3043006033522185271</id><published>2007-08-17T05:38:00.000-07:00</published><updated>2007-08-17T07:39:44.632-07:00</updated><title type='text'>New Synpl demo</title><content type='html'>Here's the first &lt;a href="http://www.snapdrive.net/files/483953/synpl-2007-08-17b.zip"&gt;demo&lt;/a&gt; since switching languages to Scala.&lt;br /&gt;&lt;br /&gt;What's in there:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a parser for many SQL and PL/SQL language elements (blocks, basic variable definitions, most types of statements, most DML stuff, procedures, functions, packages)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;a basic analyzer (can look at a PL/SQL block and report variables that are used before being initialized, shadowed variables (same names used in inner blocks), variables that are defined and/or initialized but are never used (usage is not detected in all cases, and initialization by INTO clauses is not recognized)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;a pretty printer (adds missing elements - for instance, if the user forgot a 'THEN', the parser will report an error but recover and the pretty printer will show the source with all such errors corrected)&lt;/li&gt;&lt;li&gt;a small GUI to help with testing&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;What's missing:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;many SQL and PL/SQL language elements are not identified by the parser (explicit join syntax for SQL, variable initialization at definition, PL/SQL objects and arrays etc.)&lt;/li&gt;&lt;li&gt;most of the analyzer features&lt;/li&gt;&lt;li&gt;advanced pretty printing (such as: detect total length of a SELECT query, and if it doesn't overflow the current line, print it on a single line)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Use 'run.bat' to launch the GUI. Use "File|Open" within the GUI to reach a sample PL/SQL source that demonstrates the error-recovery features of the parser, the current analyzer features and the pretty printer.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The GUI was tested with Java 1.6.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-3043006033522185271?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/3043006033522185271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=3043006033522185271' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/3043006033522185271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/3043006033522185271'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/08/new-synpl-demo.html' title='New Synpl demo'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-5547299675047203136</id><published>2007-08-16T07:42:00.000-07:00</published><updated>2007-08-16T07:51:45.488-07:00</updated><title type='text'>Synpl in Scala</title><content type='html'>I've decided to rewrite synpl in &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;. It's much better than C# for this particular task because of several reasons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;it compiles to Java bytecodes, which means I can build a JAR file and then use it to build JDeveloper or SqlDeveloper extensions&lt;/li&gt;&lt;li&gt;Scala has pattern matching and "case classes" which make it very easy to build AST trees and to walk those trees to do static analysis&lt;/li&gt;&lt;/ul&gt;So far, so good. These blog entries &lt;a href="http://blogs.sun.com/sundararajan/entry/scala_for_java_programmers"&gt;(1)&lt;/a&gt; &lt;a href="http://blogs.sun.com/sundararajan/entry/scala_for_java_programmers_part"&gt;(2)&lt;/a&gt; proved very useful as a Scala cheat sheet and saved me a lot of time.&lt;br /&gt;&lt;br /&gt;Right now a significant chunk of the PL/SQL grammar is implemented and some static analysis also works (detecting variable use before initialization). There's plenty of work to be done, but it looks like Scala is a great language choice.&lt;br /&gt;&lt;br /&gt;Probably tomorrow or the day after I'll put together a new demo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-5547299675047203136?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/5547299675047203136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=5547299675047203136' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5547299675047203136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/5547299675047203136'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/08/synpl-in-scala.html' title='Synpl in Scala'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4079147698417591386</id><published>2007-06-27T15:50:00.000-07:00</published><updated>2007-06-27T16:04:34.694-07:00</updated><title type='text'>New Ideas</title><content type='html'>I've been thinking about synpl and applications of a SQL and PL/SQL source code analyzer for some time now.&lt;br /&gt;&lt;br /&gt;Here's some of the ideas that I had (some of them don't require a parser):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;build a visual representation of the database; have the triggers/constraints listed next to each table - I understand Visual Studio offers something like this, even for Oracle, I need to look into this;&lt;/li&gt;&lt;li&gt;look into the code and find out what indexes should exist (analyze WHERE clauses and INSERTs, try to find a balance between them)&lt;/li&gt;&lt;li&gt;analyze where clauses and discover undeclared foreign keys&lt;/li&gt;&lt;li&gt;build a measure of the connection between tables (strongly connected vs. weakly connected) and then build sets of closely related tables - could help people trying to understand vast databases with lots of tables by pointing out which tables are most closely related; trying to understand a group of 10 tables that are mostly connected to one another and have a few connections to other tables is much easier than trying to understand a group of 50 tables;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;build a Django admin-like app with Java hooks (load classes for validation, preprocessing, postprocessing, custom actions, reporting); Application Express does this already? need to look into it;&lt;/li&gt;&lt;li&gt;analyze INSERTs and UPDATEs and try to infer if some fields only have a limited number of values (missing restrictions?)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Parsing the SQL queries to discover connections between tables should go beyond SQL queries in PL/SQL (i.e. use SQL queries in other languages) but the source code isn't always available. It's very rarely available, in fact.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4079147698417591386?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4079147698417591386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4079147698417591386' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4079147698417591386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4079147698417591386'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/06/new-ideas.html' title='New Ideas'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-2560107135318256919</id><published>2007-06-24T04:34:00.000-07:00</published><updated>2007-06-24T04:38:21.214-07:00</updated><title type='text'>New synpl demo</title><content type='html'>I've prepared a new &lt;a href="http://www.mymapmarks.com/demo/synpl_demo-2007-06-24.zip"&gt;demo of synpl&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's probably broken in many places as I didn't get a chance to test it properly.&lt;br /&gt;&lt;br /&gt;What's new:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the ability to 'correct' slightly incorrect syntax (such as missing a ';' or an 'if' in 'end if')&lt;/li&gt;&lt;li&gt;it can show the corrected version in a separate window; the corrections are highlighted&lt;/li&gt;&lt;li&gt;it can load/save samples&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-2560107135318256919?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/2560107135318256919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=2560107135318256919' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/2560107135318256919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/2560107135318256919'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/06/new-synpl-demo.html' title='New synpl demo'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-1956314471673282102</id><published>2007-06-19T01:10:00.000-07:00</published><updated>2007-06-19T01:14:32.198-07:00</updated><title type='text'>Plans</title><content type='html'>I'm thinking of switching Synpl to Java, so that I can write a JDeveloper extension - and later a SqlDeveloper one. The problem is that the JDeveloper extension APIs aren't that well documented. There are even two competing APIs, one that is specific to Oracle and the other which is a JSR (198, to be more precise).&lt;br /&gt;&lt;br /&gt;There are illuminating code samples for the Oracle API (which I hope can be used to develop an extension if combined with the API's JavaDoc). On the other hand, the JSR 198 seems more restrictive. One one forum discussion someone was complaining about the fact that it's impossible to write a code formatter with the JSR 198 API. I'm not sure that's true, but from what I've seen in the JavaDoc it's certainly a difficult task.&lt;br /&gt;&lt;br /&gt;Hmmm. Back to the GIS application and paper ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-1956314471673282102?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/1956314471673282102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=1956314471673282102' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1956314471673282102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/1956314471673282102'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/06/plans.html' title='Plans'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6144304081336954203.post-4544967122852101323</id><published>2007-06-14T01:42:00.000-07:00</published><updated>2007-06-14T01:48:26.547-07:00</updated><title type='text'>Why?</title><content type='html'>What's synpl? A simple PL/SQL parser (for now) and analyzer (in the near future) written in an attempt to provide friendly compile/parse error messages and warnings about problems that the compiler is silent about.&lt;br /&gt;&lt;br /&gt;Since I need to show synpl to people and email seems to be a tedious way to do it, I've started this blog to help me communicate with users willing to test synpl and provide feedback.&lt;br /&gt;&lt;br /&gt;The first available demo is &lt;a href="http://mymapmarks.com/demo/synpl_demo-2007-06-13.zip"&gt;here&lt;/a&gt;. You need Windows and .NET framework 2 to run it (the GUI seems to be running fine under Mono 1.2.4 on Debian, but it may break in the future).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6144304081336954203-4544967122852101323?l=synpl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://synpl.blogspot.com/feeds/4544967122852101323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6144304081336954203&amp;postID=4544967122852101323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4544967122852101323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6144304081336954203/posts/default/4544967122852101323'/><link rel='alternate' type='text/html' href='http://synpl.blogspot.com/2007/06/why.html' title='Why?'/><author><name>Miron Brezuleanu</name><uri>http://www.blogger.com/profile/01853055124025315229</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
