My initial attempt was to write a small wrapper library that would use aspell through a pipe. I had much difficulty in this due to the input and output streams coming out of sync for reasons that weren't entirely clear to me. As such, I grudgingly decided to look at using the c# interoperability features built into Mono and then use the aspell C API.
After reading through the documentation on the C# interop features, I threw together a small (less than 100 lines) interface to the underlying C library. The entire thing took less than half an hour, once I had a decent feel for the c# interop stuff and worked on the very first attempt. I was quite surprised at how streamlined the entire process was.
After getting the underlying API for the program to use, I had to integrate it with the two GUIs that I have for XPost. I did the NCurses GUI first because I have lately been using the NCurses version more heavily. I also had been the one who wrote the "text box" widget used in the NCurses implementation (one hadn't existed before). That led me to thinking that it would be easier for me. After some mucking about and relearning the oddities of my code (Why did I decide that the variable "word" meant the amount of the line accumulated thus far, rather than having it represent an actual word?), it was relatively easy to mark misspelled words by just inverting the coloring of them on the console. I use a private use XML character (0xE999) to denote toggles of correct to incorrect and then parse them out at during the rendering. A bit ugly, perhaps, but it means that I didn't need to dramatically change the general rendering loop. In total, there were perhaps 5 lines changed and 6 new lines to implement.
The GTK# GUI was even easier once I had read the documentation. It apparently has a concept called the TextTag that allows you to apply semantic information to a range of characters in a buffer. You can also associate a special rendering style which will be applied to tagged text automatically. Once I knew that, it was a simple matter of creating the appropriate TextTag and associating it with the buffer (about 6 lines of code total) and then setting up a callback to spell check and tag the text whenever a new character is typed (also short at under 30 lines).
I did decide to go back and add some compatibility code. Since it is possible for a system to not have aspell installed, I wrote some wrapper code that checks for its availability and disables the spell check should it not have access to the necessary libraries. In truth, I actually wrote it rather generically, so that there is an abstract SpellChecker class of which the aspell implementation is just one possibility. I then have some reflection code which iterates over all possible spell checkers and tries to instantiate each. If it can't find one, it simply returns a null checkers (it says every word is spelled correctly). Since library loading is done at run time, it is actually rather robust. This also means that if I ever decide to work on making the Windows version work again, I would be able to drop in a replacement spell checking library by writing a thin wrapper around it and letting the reflection code do the heavy lifting. I think that overall, this is a nice, low maintenance solution to the problem.
Of course, it won't save me when I write "responsed" instead of "responeded".
Published by XPostcurses