Concepts unification: registers → options ← hooks

Hello

During the last few days, mawww expressed his thoughts about future Kakoune development that I found interesting to share here.

…as registers are a bit transient and I imagine it would be useful to save a mark register into an option, that actually leads to something I’d like to explore later on, which is merging registers and options into a single concept

and on IRC:

I want to explore moving hooks to some kind of option, see if we can use options as a backend for hooks the same way emacs does, in which case it would become relatively trivial Yeah, and I would be happy to reduce the amount of separate concepts in Kakoune, especially as a lot of them converged to some list of strings (hooks and registers are basically that)

So the idea would be to reduce the number of concepts needed to grasp Kakoune and to merge registers and hooks with options.


What could be the challenges of this fusion? A few initial raw ideas to bootstrap the discussion:

Scopes

  • options : global, buffer, window, current
  • hooks: global, buffer, window
  • registers: not scoped

Some registers, like the letter ones could be forced to only be declared on the global scope. Meanwhile other register makes sense only on the buffer scope like % for the buffer name.

Namespace

If %reg{a} becomes %opt{a} does it mean that 1 letter option name are now reserved for Kakoune internal only? And what about the %val{…} expansion? Isn’t that just a readonly option in a way?

Benefits

  • Less surface area, increased orthogonality

  • A RegisterChanged hook has been asked for a long time: https://github.com/mawww/kakoune/issues/859 If registers are just options, we could then listen to GlobalSetOption instead.


How do you envision this merge? Pros and cons?

5 Likes

There’s definitely some weird things about registers that make them quirkier than options.

For example, some registers are read-only (like "% which is %val{bufname}), and the null register "_ is write-only. Would we need to have read-only and write-only options too?

Registers are currently limited to single-characters, because normal-mode " and insert-mode <c-r> only listen for one additional character. Perhaps if that character is, say, \ then all characters up until the next non-identifiery character could be taken as an option name to use as the register. For example:

i<c-r>\filetype<ret>

…could insert the current value of the filetype option into a buffer, and

"\path<ret><a-p>

…would paste all the items in the path option.

Registers that are used for prompt history are limited to 100 items, and setting the register actually appends each new value, removing duplicates, rather than replacing the old value. That’s definitely the behaviour you want for prompt history, but it’s a bit quirky for registers and would be very quirky for options.

It’s not mentioned in :doc registers, but I recall that Kakoune does not distinguish between lower-case registers and upper-case registers, while vi says that yanking or cutting to upper-case registers appends the contents rather than replacing. Perhaps if registers were options, setting upper-case registers could be defined to be the same as set-option -add which is not exactly the same as Vim, but is clearly in the same spirit.

Kakoune also does not distinguish between upper-case and lower-case marks, while vi uses lower-case marks in the current buffer, and upper-case marks across all buffers. That might be an alternative to upper-case-for-append: yanking or cutting to an upper-case register could be set-option global while a lower-case register could be set-option buffer. The quirk would be that pasting from an uppercase register would have to ignore any window-scope or buffer-scope value, and I don’t think there’s currently any way to read options from the “wrong” scope in today’s Kakoune.

I imagine the GlobalSetOption hook would trigger after a register is written to, which is fair enough. However, I think we’d still need a corresponding GlobalGetOption hook invoked just before a register (or option) is read from, in the case where a Kakoune option is used as an interface to some external information… like the contents of the system clipboard. Otherwise you’d need some timer synchronising the value a couple of times per second, and that’s pretty hacky.

yes

Specifying list of functions to run via option is much more versatile way to configure what hooks are needed and what not, especially when managing hooks programmatically

It’s a little awkward, but I think it’s doable to use a list of commands to allow modules to control order. Here’s what I’m thinking:

If a module needs to run before or after another module’s hooks, it could try %{ require-module other-module }, the try is in case the user hasn’t installed it. Then it can add its hooks, ensuring that it comes before or after the hook it wants.

This kind of thing comes up, for example, with the windowing system detection (which was solved in another way).

I also wonder - if running commands from an option becomes an idiom, will I want user hooks less?

Currently, user prompts use _ (black hole) as the history register, but if registers were options, the :prompt command could have a flag for which option to use as the history.