Moving toward arsd 11

Posted 2023-01-09

The textlayouter is in a new module, so that will be a minigui build process breaking change. Also moving forward slowly with the arsd.core module since the next tag may be 11.0, gonna try to batch as many of these little breaks together as possible.

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

What Adam is working on

Minigui text layouter

My text layouter now has all the basic features completed and I've integrated it into minigui for the X TextEdit, LineEdit, and PasswordEdit widgets. I'm quite happy with it for this basic functionality - the little things work right that didn't work in the old implementation for years. You can drag and scroll to select, it changes style appropriately on gain/lose focus, the cursor is always drawn in the right size (some of these are just fixing bugs in the old code that was just cuz that code was astoundingly bad lol) and it is fully compatible with the minigui theme system. I also fixed a few more bugs in the minigui scrollbars I came across in the process and am enhancing the theme system more since it all actually works now.

None of this, by itself, is a breaking change, but since I have one scheduled soon anyway, I had an opportunity to consider. The text layouter has almost zero dependencies - it just needs an instance that implements the MeasurableFont interface, and then it uses my Point and Rectangle structs, but those are trivial. Now, it is possible, even likely, that I'll have to add things to MeasurableFont to reach all the text layouter's stretch goals, but I'm reasonably confident that can still be done without needing to bring much in.

So, the question is: do I want to just plug this into an existing module to replace the old code in-place, or make it separate? The key cost is that it adds a new module to the build of minigui. The key benefit is I can potentially use it from terminal.d too, without adding minigui's dependency chain to the terminal chain. However, since arsd users like having the modules remain stand-alone, I'm going to avoid requiring new modules if I have another way - terminal won't necessarily use the textlayouter directly by default, but I can use a template to let people opt into it, or make a terminal_addons package similar to minigui_addons that uses it, again keeping the general dependency chain smaller.

The overall question was thus three options: 1) add one dependency to minigui, 2) add three dependencies to terminal, or 3) just use it in minigui or simpledisplay and give up on the console mode idea. (Well, or 4, what I've done a few times, copy/paste some code.)

Normally, I'd actually go with option three, since the terminal thing isn't really THAT important. I don't know if I'll ever actually even do it (and what I'd really like is to bring terminal.d's LineGetter into minigui moreso than minigui's into terminal, but there's not really a practical way for that to happen without breaking changes to both, which got voted down). But, since I'm adding the arsd.core module anyway, I did decide to just go ahead and do it this way.

Will I still allow a version switch to keep the old one? I did for now, but I think I'm going to axe it soon, since the new class is just unambiguously better than the old one.

The end result is a minigui hello world build goes from:

dmd yourfile.d minigui.d simpledisplay.d color.d

to:

dmd yourfile.d minigui.d simpledisplay.d color.d textlayouter.d core.d

Or, of course, just plain:

dmd -i yourfile.d

I should note another option is to move the TextEdits to an addon module, but that is also a breaking change (in fact, a much bigger one, requiring user code changes instead of just adding a file to a download+build command) and goes against my normal policy of useful things just working whenever reasonable, so I don't want to do that either.

None of this is released yet; I am going to batch as much as I can so it might be a while longer before I push the fixes with the build process breakage to git.

I also intend to write a few themes to pre-package in minigui so people can pick from a few and I want to do a few more keyboard enhancements with menus and drop down boxes if I can find the time before the release.

Streaming XML parsing

I added a virtual function to arsd.dom's Document class which is responsible for building the dom tree. The default implementation is just parent.appendChild(child), but if you were to override this, you could just process child as it comes and not append it to the tree. In theory, this should reduce the total amount of memory needed to process a large document, since only the current parent chain would actually be kept in memory; most the tree can be garbage collected as you go if you don't append it. I haven't actually tried it on anything large yet though, but this was the simplest change that could possibly enable such things.

arsd.core

Well, the big thing for the 11.0 release is the "core" module, which I still haven't really started. I am still just thinking through the design.

Again, the key goal here is to integrate the repeated event loop code. Currently, there's some very similar code that appears in several modules for overlapped i/o and timeouts. I offer integrations with simpledisplay's event loop in most of them via template functions and I'm actually reasonably happy with it, but it is also a bit of a pain since I can't just use the functionality, it all has to go through the extra layer.

A secondary goal is to unify some other shared concepts like validation UDAs between the web and gui worlds, and to improve error handling with some out-of-the-box helpers. My current thing is to re-define some string udas, but I'd rather make them appropriately typed and formally shared.

The benefits are individually modest, but I think they add up to a positive result against the downside of having to add the new module to user's builds. And, just like with the textlayouter, there's good potential for bigger improvements in the future.

In the past, I've written about some concepts: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_08_29.html#what-adam-is-working-on and http://dpldocs.info/this-week-in-d/Blog.Posted_2022_04_18.html#thoughts-on-async-io notably, saying I want to keep up with Javascript and Windows' designs, as both of them have some practical victories.

And while thinking about pluggable tasks this week, another parallel came to mind: the Unix pipe.

I was trying to think of a generic task that can report results back to its parent. First idea is to use a delegate that runs async, and its return value is stored. But what if you want to send multiple values as a stream? Sebastiaan's concurrency lib allows this too with a parent-provided callback, so I started jotting that down too, then thought about putting it in a ring buffer... and needed a way so signal buffer full and cancellation requested... and at that point, I realized I had created a unix pipe! The key difference being that it has more types than just bytes, since the ring buffer can use whatever D type you want.

Now, do I want *everything* to go through the pipe structure? For example, is a file read a little pipe that writes the chunk through it when it is done? Does it keep streaming automatically until cancelled, or do you keep re-issuing read requests? Is a timeout just something that closes its pipe when its time is up? All certainly possible - indeed, Linux has things like timer_fd that do work like this. Do you want to just actually use an OS pipe when available?

Still a few things to figure out, but the fact that I landed on something so recognizable is probably a good sign - it isn't exactly novel, but I know I've circled back to something that has a proven track record, so it probably isn't wrong either. And now I can start borrowing other ideas straight from pipes - if a receiver dies, the writer can get a SIGPIPE to cancel it. I can make sure the waitForFirstToComplete function indeed works like the poll(2) family of functions. I can have concepts like closing pipes to signal cancellation separately from either magic data or separate messages. (Indeed, is it still waiting for one to complete, or just waiting for one to signal progress? Speaking of signaling progress, do you want a separate channel just for indicating progress bars and debug logs? Perhaps...)

Similarly, would I want to adapt existing things like the http module to call progress callbacks? Or, perhaps more accurately, send progress down a pipe, which calls the callback when it is received? There's some nice potential there to maintain thread affinity too.

I worry that I'm going to commit to something then regret it shortly thereafter, but I suppose that's better than just staying in analysis forever and never shipping anything. So soon I'll have to do something - the release can't wait forever - and recognizing that I'm reinventing Windows I/O and Unix pipes helps bring things together.

Though I might also just tag the release with a stub module and fill in the rest later. Remember, the main breaking change is adding the file moreso than what's in it - many users won't even notice since it only affects certain build styles!

Other pending things

I'm still halfway through refactoring the arsd.simpleaudio synth functions and the arsd.game library, and I am also still planning on new validators for cgi and gui - which can be done individually but I also want to combine them.

I wonder if the html5 pattern regex and the D std.regex syntaxes are compatible. If not, does the dmdscript regex engine still work with all this? Would be nice if I could just use the same string without translation.

Anyway, still a lot of work to do. I'm optimistically gonna try to tag it in early March, but we'll see how it goes.