orbitalquark / textadept

Textadept is a fast, minimalist, and remarkably extensible cross-platform text editor for programmers.
https://orbitalquark.github.io/textadept
MIT License
640 stars 38 forks source link

Why hide brace_matches? #318

Closed newbthenewbd closed 1 year ago

newbthenewbd commented 1 year ago

Hey,

Coming from the presumably familiar position of dissatisfaction with all editors except Notepad++.. all the while using macOS and Linux, going down the Scintilla rabbit hole, configuring SciTE to my liking, then finding it a bit limiting, Textadept looks very promising, I shall just code in what I'm missing...

...but, in 11.4, I could trivially reimplement SciTE's braces.sloppy=1, which I very much liked (editor hopping pays off, sometimes):

events.connect(events.UPDATE_UI, function(updated)
    if updated & 3 == 0 then return end
    if textadept.editing.brace_matches[buffer.char_at[buffer.current_pos-1]] then --doesn't seem to crash without a buffer.current_pos > 0 check, huh
        local match = buffer:brace_match(buffer.current_pos-1, 0)
        local f = match ~= -1 and view.brace_highlight or view.brace_bad_light
        f(buffer, buffer.current_pos-1, match)
    end
end)

...while in 12.0, it seems that brace_matches is planned to be local.

Naively, the list is quite easy to recreate, but as it's still in use internally and considering that I can't be the only one doing something with it, isn't hiding it at odds with the goal of giving lexers more control over what is matched? Or am I missing something, which I probably am? :)

Thank you so much for Textadept!

orbitalquark commented 1 year ago

I don't think you're missing anything. I had not considered a "read-only" use case of brace_match. One issue though is that it is constantly re-created. If a user were to "cache" the table (i.e. assign to a local variable), that table would become out of date pretty quickly.

In general, if a feature is not used outside of a module, it is not exposed. In 11.4, brace_match is configurable, so it needs to be exposed. Now that it's auto-configured in 12.0, I didn't see a need for exposure.

I will think on this.

orbitalquark commented 1 year ago

After thinking about this, for now I would just re-create a local copy of brace_matches (optionally asking the lexer if it includes angle braces). The keys are byte values and I am trying to move away from this value type in other exposed tables like auto_pair and typeover_chars for simplicity and user-friendliness. I would prefer to keep using bytes in this particular table because it's fast and wouldn't require a byte to char translation on every ui update. I could perhaps do some trickery with metatables, but that seems to be more trouble than it's worth. Sorry for any inconvenience :(

newbthenewbd commented 1 year ago

Well, thanks a lot for thinking about it! :)

To give kind of a perspective/feedback around these things, should it be worth anything -- myself somewhat devoted to designing a library (that still isn't there, as my barren GitHub profile shows :smile:), I really feel for that labor of love in making the API nice, consistent, and featureful. But then, as an end user, I often focus on... working around the API. Or, as many would call it euphemistically, "thinking outside the box" - should these terms not refer to wholly the same thing, sure do they lie close. The reason for this is pretty well summed up by the claim that the author of the API didn't think of what I specifically wanted to do.

But is that API horrible then? Am I blaming it for these numerous oversights? No, absolutely not! On the contrary: it is a task of ridiculous complexity to thoroughly implement an API that intentionally covers all the bases, and then also is any easier to use than whatever it tries to abstract away. In fact, if it was so great, whatever the users were to accomplish with it would be a mere trivial step away, easily achievable by the author of the API, who also has the benefit of knowing its ins and outs - and yet, the users of that API tend to create a whole new wide profession out of its usage.

Long story short, I should not hope that the nice, abstract API will provide everything - not without severely crippling usability through excessive complexity. In the case of code, how that works out in practice is that what I want to do is often eerily similar to something that the API provides - maybe I want to create two windows, but it only supports one, maybe I want a 24-bit texture, but it gives me 32 bits. Things seemingly easy to add to the API in isolation- but all the possibilities combined, added to the API, may just make it a bloated piece of junk. Not to say that the above two should be the things to omit in display management... :) But example/>.

So what can I do when this happens? Naturally, create my own functionality that mimics what the API does, but with the altered characteristics that I desire. Permissive, open source implementations may make it very easy - copy, paste, tweak. Or cunningly alter the data structure at just the right moment. Only if absolutely no other choice at all, fork. The paradox is that - while the ease of doing this will make me think all the better of the API - its designer is going to tremble at the mere thought of my perfectly working exploits... and, if they can, sometimes work hard so that none of this is possible again, perhaps with a couple new features introduced instead.

A common reason for the API designer's anxiety is that the terrible hack not supported by documentation, which I dare call a satisfying solution, may stop working in the future when the undocumented implementation changes. In many contexts this is a valid concern. But is it a concern when I am in full control of the whole software stack affected? Proper, documented APIs often also have to go through breaking changes, in order to improve. If I am not so shortsighted as to expect the name of an inner variable to never change, if it wasn't locked out on purpose, any such change will probably be trivial to cooperate with - simply by observing how the source of my inspiration changed - and not actually nearly as frequent as the designer fears. If this wasn't the case, I would've forked.

As to what happens when it is locked out on purpose? Well, my source of inspiration stays the same, but suddenly I cannot use it. A function that I would need is private, or the data structure is protected memory, or... Not to diminish the actual utility in engineering of such features of computers. So I, the user, have to find another working way - at this point, I, the API designer, am roses happy, but that won't be for long... because the new way will just tend to be so much worse. A fork with a single tiny change that never synchronizes again, or an allowed solution but with twice the overhead are the better among them - I just imagine a certain comic on someone's broken workflow, but alternatively captioned: "Because keysPressed[] was local".

Oops, I wrote an article again. All that to say, I believe that locking out the actual internals, even should they be undocumented, difficult to use or what-have-you, is hardly a one-size-fits-all solution, this to mean in a diplomatic way that in reality, it fits very few... :)

But anyways, the way Textadept works actually makes keeping a tiny, updated-when-I-need-the-updates fork a breeze, so, coupled with my use of some Scintilla patches, am doing fine doing just that for now lol. Thank you!