I rewatched Wednesday's 4coder stream (the one with the callback rant in the prestream)

I noticed that the "custom commands" of 4coder are all callbacks; they just don't have an explicit void* userdata in them (I believe that is exposed through a set of helpers that let you bind and query void* of various commands). Yet as soon as he started on 4coder he didn't speak about callbacks and their awfulness at all.

Though you can think of it the other way. The 4coder application uses the api implemented by the custom dll. But then it provides callback so the dll can do buffer manipulation (in the api parameter of the custom commands).

So no matter which way you turn it there are callback being provided across both sides of the API boundary.

Then the question becomes how do you eliminate the callbacks in such an api structure.

One way would be to put the dll in control of the event loop, let it do a NextEvent(); in a while loop and then do the processing.

Or let the custom commands return a data structure that says what the next step should be (do a command and then callback or wait for next keypress). However that could turn into a lot of back and forth when the dll information is not immediately provided to the callback. It turns getting that information into a async operation (without the C# async/await support that lets you write them as if it was sync).

So getting the data from the current buffer and then doing some processing would turn into:

next_command Result={};

Result.Action = READ_BUFFER;
Result.Range.Min = Mark;
Result.Range.Max = Pos;
Result.NextCustomCommand = HandleReadData;

return Result;

Then each custom command would have an extra parameter ActionResult; for HandleReadData that would be a const char* with the read data. Adding a void* userdata would then be needed if many commands like that are needed; for example for search&replacing in all files in a folder.