Arrival of Overlord.
What started out as “a few tweaks” has ended up becoming something of a gut and redevelop. I’m not saying there was anything wrong with the code, it just didn’t lend itself to easily doing what I want. Now that I’ve spent quite a bit of time with it I can understand how it happened. Barb can feel free to correct me if I’m wrong, but it looks like Diamond was originally developed as a single file editor and tabs got added in later. Everything was kind of tightly coupled to MainWindow. Since this isn’t my child I have no qualms about genetically altering it. :D
One of the big things I learned, before swapping out the incredibly slow drive for a slightly faster drive is the JSON writing was both happening too often and in the main event loop. Qt examples you find on-line and in many official books are generally really bad examples. I’m sure the people who wrote them didn’t set out to create a bad example, they just did. You have to get a formal education and you really need to go work in the embedded systems world for a while.
Cardinal sin 1:
Performing significant I/O in the main event loop.
Every example you find on-line will do this. I’ve even done it in some of my books because QSqlTableModel forces it on you unless you do a lot of work to thread things off. When you test with low powered low end equipment you quickly learn why you never do significant I/O in the main event loop.
That’s what lead me to altering the DNA of the child. Things were tightly coupled with MainWindow and there was casting of parent to MainWindow pointer. Hey, if I was writing a personal experimentation project for myself and letting it evolve over time, I might have done the same thing. If it wasn’t getting in the way of my adding Themes, I might have even skipped it.
Another thing I wanted to change was the use of structures to store settings. I converted all of that to classes because classes can assign default values meaning there wasn’t another entity that had to know the default values. It also means you can’t accidentally end up with an empty structure due to a logic bomb.
I created an Overlord class. (I was going to call it BenevolentOverlord but I think that might be trademarked.) Overlord is a singleton class who is charged with holding all of the needed Settings, Themes, KeyDefinitions and other such information needed by every tab in MainWindow. It is also charged with keeping track of when these things have changed.
Rather than immediately updating everyone (for most changes) it waits for a timer to fire and signal out with a new pointer. Each little tab then checks to see if anything it needs to worry about has changed. There is another, slightly longer timer, that controls writing to disk. I forget what the values are right now. I think 3 seconds for the update signal and 5 seconds for the disk write. When something major, like a Theme is changed, it forces out and update to everyone.
Creating the Overlord class got rid of the need to be passing around and casting MainWindow pointers. Replacing the structures with classes helped encapsulate the initialization values and allowed me to create comparison operators and such.
Won’t be diff-able
Some of the original source files went away with the creation of the classes. Others went away when their code was merge into the classes using them. Hopefully this can be merged back into Diamond when I’m done. It certainly won’t be Diff-able. Way too many sweeping changes and more on the way.
For right now I’ve got the “default” theme set to COBALT, just because I like it. The CLASSIC Theme is still there.
Constant color added
If you compare these screen shots to what is currently in the main code base or one of the pre-build binaries you will notice that I’ve added a color for Constants. It doesn’t work. I suck at regular expressions and the ones I’ve stolen from both books and on-line don’t seem to work with the syntax highlighter. The purpose of the Constants color is to highlight all numeric constants. That purpose has yet to be achieved. It’s the classic problem of 0 vs. capital O and lowercase l vs. the digit 1. Some fonts make that impossible to see.
Default font changed
Oh yeah, I changed the logic for the default font because Courier New was simply illegible on Ubuntu 20.04. For now it is set to Monospace which exists on every OS officially supporting Java.
If I don’t run out of time and energy I’m going to thieve the code from my xpns_qt3 project for finding fonts so the “default” can be whatever “good programming” font is on the system, walking down the list from best to worst.
I know, back in the day Courier New was one of the few fonts you could reasonably expect to exist on all PC based platforms. I used to default to it with my Zinc stuff years go. Once Java had its flash-in-the-pan Monospace became “available by default” on most platforms and it usually renders better. Honestly I don’t know what happened with Ubuntu. Courier New has never looked that bad on any other platform. I generally prefer “Liberation Mono” that gets installed with LibreOffice, but not everything, notably Windows, pre-installs LibreOffice.
It may not be completely obvious, but I reworked DiamondTextEdit so I could put it directly in this dialog instead of the basic QSimpleTextEdit. The source code being displayed is pulled from the resource file. You should be able to paste/type whatever text you want to try out.
I made the effort because I wanted to not only let you set the gutter colors, but see them real time. Normally the class receives a signal from Overlord telling it the theme has changed. The class then updates its colors and highlighting. The same slot Overlord normally gets connected to can be called by the dialog to show potential changes without actually changing anything in Overlord.
Current line color now only applies to background
This may not be obvious, but I changed the syntax highlighting so the Current Line color only applies to the background like most other editors. You just have to pick a good color that works well with your Theme. Something I will have to do for JUSTDARKENOUGH before I ask for this to get merged in.
You might want to zoom that SOLARIZEDLIGHT picture because it came out nice. I still prefer COBALT most days, but I really like how this Theme turned out. Tango didn’t turn out too horrible either.
I guess I’m just old. Most days I prefer looking at this.
Initializer list vs. function
Someone at some point might want to take it upon themselves to fix the C++ regular expression for functions so that it can differentiate between an initializer list and an actual function. Emacs can do it so one of us should get Diamond to do it as well.
Create your own themes
No, I’m not an evil dictatorial overlord. I didn’t design this to say “Here’s the Themes. That’s it!” You will find “Copy,” “Export,” and “Import” buttons on the dialog. “Delete” only becomes enabled when one of your themes is the current one in the combo box. The pre-installed are there for life as long as you don’t go out and do something evil to your config.json file.
Copy prompts you for a new Theme name and checks for collision. It them makes a copy of the theme and sets the new Theme name to the current entry in the combo box. (Until you save your active Theme hasn’t changed.)
You will notice that the “Delete” button is now active. BlueFred can be nuked.
When you delete the Theme the dialog believes is “currently active” it will switch back to the CLASSIC theme.
NOTE: All Themes are currently stored in an array in your config.json file. Don’t go hog wild creating/importing them. Every Theme will add some data to the I/O operation of that file. Yes, if you have a be-all-end-all system with a blindingly fast I/O subsystem and a hybrid drive with a Gig of write cache you probably won’t notice much. I’m deliberately testing with sucky I/O. When I get Diamond “where I want it” I’m going to load Ubuntu 20.04 LTS on my ancient Acer Aspire One 722 that I wedge 8Gig of RAM into years ago and test it on there.
If it runs “good” on that with large (> 2000 line) source files, it runs good.
Custom themes repo
At some point, if this code gets merged into the baseline I expect the CopperSpice project will have to put up another repo or download area to keep the community created Themes all in one place. I also suspect that if someone absolutely must have every Theme out there someone will have to break Themes out into a sub-directory under ./config/Diamond and process individual Theme files. That is farther than I cared to go. Adding Themes was something I did for my eyes and to get familiar with the code.
“Export” prompts for a name and uses the extension .diamond_theme. This creates a JSON file containing the theme information. Import also prompts for you to choose a file and only looks for files with this extension. The name doesn’t have to match the Theme name as the Theme name is contained in the file.
Sadly my newGrey import test theme looks better than the GREYGRAY default Theme. If you try to import a name which existed prior to the dialog opening you will be prompted to assign a new name.
Small import bugs
I just noticed a pair of small bugs while testing this to create the screen shots. It does not check for a collision with a just imported name and if you cancel out of the new name prompt it imports a blank name. Gotta remember to fix those.
There is a lag between closing the color dialog with the Save button and the theme being seen.
I’m not going to burn anymore time on fixing that. The previous version of Diamond popped up a dialog telling you to wait until it got all done repainting things. I generally understand what is causing it, but don’t want to burn time on it when there is so much else I want to do.
The reHighlight() has to run regular expressions over the entire file. This is happening in the main event loop. If all of your files are under 500 lines the redisplay is almost instantaneous. There is some tipping point after encountering one file with 1000+ lines where it becomes a lazy worker in a tough union.
Qt historic memory issues
Qt has historically had memory allocation issues. Not the kind of issues one normally thinks of when one hears that phrase. It tends to create a lot of objects. On certain platforms, notably embedded platforms using low power consumption memory to preserve battery life, it burns you.
I was on one embedded system project where all of our images came in SVG files. SVG files are basically XML. So far no biggie. We were getting one file for an entire screen’s worth of images. Every time we loaded a single image it had to parse the entire XML document, then find the image we wanted, the dispose of the document. We had something like a 5 or 9 minute cold boot time. One guy spent a week/weekend changing the load so it loaded all of the images from each file before moving to the next. Thankfully we had the RAM because we were loading images that only got used during error conditions and images for screens that may never be displayed.
My gut tells me something like this is happening in the syntax parser class. Oh! That reminds me.
Overlord can pre-load syntax
One of the performance lags I did fix. With this sucky I/O I really felt it. Each time you changed a Theme it was loading the JSON for syntax highlighting from a file. Keep in mind there is only one color set in the existing editor so this wasn’t an issue or design flaw. It became an issue when I added Themes and was bouncing around between them testing.
My solution for this was to add options for Overlord to preload certain syntax regular expression syntax files. Every tab requests this information from Overlord. If Overlord has it, presto! If Overlord doesn’t already have it loaded, you pay the price once for the run. It is loaded and will be forever available. Yes, I put my checkbox for EDT numeric keypad support here as well. I don’t have that working the way I want yet.
This post is long enough.