Kakoune needs a real scripting language

I love Kakoune’s goals of Unix integration and “orthogonality”, as Mawww puts it. However, I think there are some serious problems with this particular goal, stated in the design.asciidoc page:

No integrated scripting language : for the same reason as binary plugins.

Firstly, that’s simply not true. Kak files are scripts. Look at the auto-indent hooks, for example this line which indents after an opening expansion bracket in Kak files:

try %{ execute-keys -draft k <a-x> <a-k> \%\w*[^\s\w]$ <ret> j <a-gt> }

That “try” command is effectively an if-else, which relies on the success or failure of the <a-k> “keep matching” command to proceed with indentation. This doesn’t simplify things by integrating with Unix… It’s the complete opposite. It’s an if-else statement with horribly unreadable syntax. That statement should look like this (pseudocode):

if (line-above-cursor ~ \%\w*[^\s\w]) {
  indent-cursor-line
}

This is not elegant either:

eval %sh{ if [ condition ]; then echo "execute-keys <blah><blah>"; fi }

If Kakoune is to adhere to its own philosophy, it needs to be configured using a proper scripting language like Python or Lua. On the other hand, if Kakoune is to continue using its own scripting language, as it currently does, that language needs proper control flow and readable syntax. Right now, we have neither. Kak script is in a painful limbo between those two options.

10 Likes

Is it?

If you want an editor that is configured with a real language go for Emacs. Lisp is great, Emacs Lisp is easy to pick up, extension possibilities are endless. On the other hand this involves learning new language, which isn’t great for users who need a tool.

Try blocks that you’re referring to as if aren’t really if, because they will silently fail without causing troubles to working Kakoune even if you will put complete gibberish inside it. if will. This is major difference between if and try.


Kakoune’s approach is to be as minimal as possible to allow as much extensibility as possible. That’s why it allows shell expansions, which can call any language. Many scripts use awk and perl to do heavy lifting based on Kakoune’s state that is exposed to shell via variables.

This way you don’t really need scripting language. Trust me, I’ve written several plugins for Vim, several plugins for Kakoune, and some code in Elisp, and I can say that Vim < Kakoune < Emacs in terms of configuration language. Given that VimScript is a complete language, it still my least preferable, because it is a horrible language. Kakoune has no language but it allows me to choose what language to use with it, because it provides POSIX interface. Emacs is a lisp machine itself so there’s no option for other languages really (yet you can use them but why would you?).

Sorry if I may sound rough, didn’t really meant anything like this

3 Likes

A lot of editors (and other tools!) are designed around the Emacs model, where there is an internal data structure (the “buffer” or “document” or whatever), an API for manipulating that data structure, and arbitrary keybindings that call arbitrary APIs. In such a model, the API is the “source of truth” for how to interact with the document, and the keybindings are fluff that could be changed around or entirely deleted without changing how the editor really works. For that kind of editor, it’s important to have a decent scripting language, because that’s the primary way the APIs are presented and used.

Kakoune is not designed around the Emacs model. Kakoune is fundamentally a modal editor; not just in the interface, but in that there’s one language for editing a buffer, and another entirely separate language for managing buffers. In Kakoune d is not just a keybinding mapped to the “delete selection” editor function, d is the name of that function. A string like k<a-x><a-k>\%\w*[^\s\w]$<ret>j<a-gt> is not just an inscrutable encoding of a “real” program, it’s the exact source-code of a program. If it’s hard to read, it’s because the programming language is optimised for interactive use rather than to be gazed at holistically.

If you’ve studied music theory, there’s all kinds of really interesting and important stuff like chord progressions and tonal modes that’s very difficult to see when you look at a piece of sheet music, because sheet music is designed to show you the notes to play at a moment in time, rather than communicating the larger structure of the music. If you play the wrong note, you’ll hear it immediately, even without music theory training. Likewise, Kakoune’s editing language shows you the key to press at a moment in time, rather than communicating the larger structure of the operation. If you press the wrong key while editing interactively, you’ll see it immediately, even without computer science training.

The disadvantage of Kakoune’s model is that understanding other people’s code is weird and difficult, and writing sophisticated code is also weird and difficult, especially for people used to regular programming languages. The big advantage is that if you can use Kakoune, just about any common editing operation can be typed after execute-keys and you’ve got a plugin. You don’t have to worry about looking up method names or flow-control or variable naming, it works exactly like it would if you typed it out manually. Kakoune deliberately ignores the problems of medium-scale and large-scale plugins to make small-scale plugins as easy and straight-forward as possible.

5 Likes

I’ve seen a few relying on awk. Do you have examples of plugins written in perl?

I don’t know many Perl based, but kakoune-snippets and kakoune-gdb use perl. I’m using Perl in some of my plugins, for instance langmap.kak uses it to generate mappings, and plug.kak to sort paths by depth. Kaktree uses Perl quite heavily too

How does this adhere to the philosophy? I actually think the philosophy is adhered to very clearly by the way Kakoune is doing it now. That said, I am not sure the adherence to that philosophy is always a good decision in the face of pragmatism.

Which violates another Kakoune design goal of Unified interactive usage and scripting – if you want there to be a command like indent-cursor-line – or perhaps you just meant the commands to do that would go there?

Agreed. That said, I am not sure we are in a worse place than viml (vimscript) but it can be a dark, confusing, escape oriented slog.


I think rather than focusing on trying it go against the design goals, we should try to improve on the pain points. For example, if you like Lua or Python – how could we make it easier to interact with them in a performant way? Where are the wins we can do without violating the core of Kakoune?

1 Like

One idea could be to allow declaring custom expansion types.

declare-expansion lua '/usr/bin/lua'

evaluate-commands %lua{ print("set-option makecmd '" .. os.getenv'kak_buffile' .. "'") }

Of course, this is just a shorter and more efficient version of %sh{!#/usr/bin/lua ...}, so maybe the value add is not large enough…

1 Like

Would be interesting in languages that have repl or some way of keeping the process running. If we could use a command like that to avoid startup cost of the process might be worth having, which would make it a thicker wrapper than just %sh{!#…}

1 Like

I honestly think there are more options to consider before adding a scripting language to Kakoune. Since the idea here has been to leverage POSIX, and POSIX has a scripting language, a better idea would be to give shells more access, via POSIX-y means.

For example, if Kakoune started storing its registers in $XDG_RUNTIME_DIR/kak/<session-name>/registers/<regname>, and reading these files when it needed the values, then shell scripts (and other external programs!) could manipulate the register contents. This could remove features from the kakscript (presuming it is done well), which is more inline with the stated design philosophy of minimalism and integration with other tools.

Registers, specifically, might be terrible idea; however, it is clear that if we are creative, we can come up with solutions in this space without adding a built-in scripting language. In fact, I find Kakoune inspiring in the interesting ways it has leveraged POSIX to do less with more.

And why am I opposed to a built-in scripting language? Because it would kill Kakoune, as a project. There are only a few developers to maintain the code here, and they could easily get sucked into maintaining a scripting language when they should be thinking about how to edit text. Just as in business, a project should pick one thing to do and do it well, and the rest should be outsourced.

4 Likes

I have been for a long time on the fence about a suggestion by @alexherbo2: have alternate names for normal mode keys, such as <keep-matching>, <split-on-regex>, <align>

I think this would go a long way into making normal mode scripts much easier to understand, but would unfortunately decorelate the interactive keystroke from the scripting command name (although using the key directly should still work, the idea would not be to introduce an indirection from a to <append>, they are the same key.

As said initially, I am not sold on this, but maybe it would be a good direction to explore.

Isn’t the next natural step to have alternative names for everything… then you might as well bind keys to those names? I hate to invoke the slippery slope argument, and I am not even convinced it is the wrong decision, it just seems like doing it just for normal commands would be an inconsistent line in the sand.

I think the argument for there being one true definition of a thing is still valid, and I am not sure if it is actually the most confusing thing. Speaking personally, I am far more likely to get stuck on escaping (or forgetting to escape) something than anything else.

I think scripting the editor is an issue, but obviously not a crippling one, we have a lot of great plugins.

@eraserhd’s very P9 inspired idea of allowing a lot of work to be done via a mock filesystem is interesting and possibly very complete solution to a lot of hard problems, from history management to recent files to register handling to (you name it) – again following the P9 path.

Another interesting idea (I think) would be building on @bugabinga’s declare-expansion idea, possibly allowing the thing to be run persistently for languages with like a repl, so your plugin could do declare-expansion lua-for-plugin '/usr/bin/lua' and that would basically just shove what is run onto the repl and get whatever the result is back. Would like the script hide some state in the running lua.

Yeah I kinda liked declare-expansion idea, which would turn a some of %sh{perl -e ' ... ' } into simple %perl{ ... } which also will allow to use single quotes inside Perl expansion without pain and properly highlight Perl code inside in the same way how it is done for %sh{} expansions.

@eraserhd you look at http://man.cat-v.org/plan_9/4/acme – a lot of great ideas to be borrowed.

EDIT: The more I think about it – the more I think @eraserhd has hit on a fundamentally amazing idea that would make communicating with Kakoune from the shell amazing rich, powerful and shell friendly. I think a rich file system interface is the solution to almost every problem I have had.

1 Like

I think adding scripting language to Kakoune is bad idea. Sometimes when I see some crazy escaping i really miss Lua (or other embeddable language) but at the end of the day I think it’s better to stick to unix philosophy.

Hopefully we will avoid creating huge, Kakoune-specific plugins this way. Take a look at Parinfer rust. It’s great example of plugin implemented as external tool that is usable from Kakoune and Vim (probably other editors could use it as well).

Kakoune as 9p server is thrilling idea but I see one issue: portability. Currently you need POSIX enviroment to run Kakoune, this model would require 9p client (or FUSE). Honestly something more mundane like built-in http API or extending JSON RPC would be much more portable (but less cool).

Basically this. It’s obvious that implementing some things in shell is crazy so we should think how to improve integration with external languages and tools.

I’m not sure what to think about declare-expansion idea yet. It’s interesting direction (especially persistent interpreter connected to Kakoune) but it doesn’t solve all the problems (you still need to escape }, need to communicate via env variables, what happens if you call blocking function, etc).

1 Like

There’s work going to make a Emacs package that will use it! Can’t wait

Let’s face it. If we want to write scripts in different languages without escaping hell, and use these scripts in plugins we need a system that will be able to track where plugin was sourced from so we could have consistent relative paths to our scripts from that directory. My plug.kak could do it, I suppose, but that’s not the right place to fix this problem.

For example. I see no problem implementing something in Perl, because we can use perl -e in shell expansion. But if this Perl script will get complex it is more appropriate to move it to some script.pl file and call it instead. But how? We need to know a path to the sctipt, so we could call it in shell expansion like so: perl /any/path/can/be/here/script.pl. What solution could be used here?

Maybe some variable that Kakoune will manage for us, like $kak_script_path, for example let it be ~/.config/kak/autoload/plugin/rc/, so we could write %sh{ perl "$kak_script_path/../perl/script.pl" }?
This way we can have proper file structure in plugins with any kind of language used in it.

Same goes for Python, Lua, LISP, and other languages, even compiled ones because we could call executables. Sure we can place those scripts in our PATH, like it is currently done for some Rust based plugins like kak-lsp or Parinfer-rust, but I don’t know if it’s suitable in terms of security, and name conflicts if all plugins will place their script.pl, script.py, script.scm in PATH.

Well it is already possible with $kak_config/perl/script.pl, isn’t it?

no. I can put script.kak anywhere and source it with source /home/user/kakscripts/somescript.kak in kakrc for example. It is not located under $kak_config and nothing prevents me from it.

ah I see, yeah that would be useful

Also it is the same principle that Kakoune uses. We know that kakoune uses relative paths from it’s location to get its scripts. I think this could be exposed for scripts too.