Kakoune needs a real scripting language

I don’t believe a persistent interpreter should be implemented in kak.
There are just too many out there, all with their own special version of hell.
Imagine a persistent lua interpreter and how to stop each script/expansion snippet from trampling over each other’s global state.

The problem of “having to escape {}” is solved by kak itself, IMO: %sh{...}, %sh(...), %sh[...],%sh|...| are all the same to kak.
Simply pick one that does not conflict at the time.
The beenfits of declare-expansion I see are:

  1. Efficiency
  2. Semantic clarity

Although, as said, I am not yet sure, both those benefits are large enough to warrant changing kak’s code.

I am not sure I understand the benefit here.
Would that mean, that files in PATH are not accessible anymore in shell expansions?
Is this to keep the plugin ecosystem clean?
Otherwise, where is the difference to an environment variable KAK_PATH, that I manage myself?


Another idea to mitigate the “escape craziness” in kak meta-commands (commands emitted in shell expansions) is for us to only use the long string syntax (%{...}, %#...#) in those meta-commands.
That way one could always resolve conflicting escape characters *1.
It doesn’t help, that the % is already meaningful in printf, but hey … it is manageable.

@eraserhd Thanks for providing thoughtful push back. I appreciate the attitude of being careful with the accretion of features. Your suggestion reminded me of a blog post where someone outlined an idea of a programming language, that would basically store its state (stack, program counter, variables,…) as directories and files. 250bpm

*1: Except, when you write shell expansions, that return commands, that use shell expansions … :face_with_monocle:

$kak_source contains the path of the currently being sourced kak script. I think bundling helper perl/python/… scripts in a well known relative location and sourcing that. I think I discussed it some time ago about kakoune-gdb.

I didn’t knew about $kak_source but it seems it is a thing that doesn’t available when we running commands defined in the script unless we store it some option:

filename vaiv.kak:

echo -debug %sh{ echo $kak_source } # prints path to vaiv.kak in debug buffer

declare-option str vaiv_path %val{source} # stores that path

define-command vaiv %{
    echo -debug %sh{ echo $kak_source } # prints nothing
    echo -debug %sh{ echo $kak_opt_vaiv_path } # prints correct path again
}

Documentation says:

path of the file currently getting executed (through the source command)

Are there any caveats? Like, can I use this method in my plugins to separate them into files on different languages? Or there is a way in which this variable can be left empty? I guess it’s a question that should be addressed in this Manual++ topic (which seems to be lacking some value expansions, @robertmeta!)


@mawww

Hm. I was experimenting with my langmap.kak plugin, and the source path part works fine - I can execute my Perl script no matter where plugin is placed. But the Perl script itself doesn’t work anymore.

I’ve pushed it to test branch. Not sure why it isn’t working.

Previously I used STDOUT from Perl to set options and execute map commands, but now, when perl isn’t inline Perl (perl -e ' ... ') I thought that I should pass $kak_client into it, wrap all inside commands into eval -client $passed_client, and pipe its output to | kak -p $kak_session, but that’s not working for some reason. I’m not asking you to dig in my code, but maybe I just missed something obvious.

Edit: nwm, it was my mistake.

evaluate-commands %sh{
    perl -e '
        # welcome to single-quote escape hell
        # also you need to escape any delimiter which you've
        # used to declare the shell expansion here, 
        # and perl can use them all, unless you've used
        # something like this: %sh£ perl ... £
    '
}

The benefit is that you can have access to any file relative to the script you’re currently using.

Nope.

Yep.

It will be different for every script.

Perl is a though case, every char means something.
I see your point.

I don’t know why, but I expected the following to do the obvious thing:

evaluate-commands %sh{
  #!/usr/bin/perl
  print "echo Hell"
  print "o"
  print "World;"
}

Got it.

For what it’s worth I don’t edit the perl script manually in there (that would be hell with the escaping), I “compile” it from a separate gdb_output_handler.perl file using an adhoc build script. It also lets me test the perl script in isolation. But yes I should probably completely split them and use $kak_source instead, although I quite like that the gdb.kak is pretty much self-contained.

That’s basically what stops me every time when I want to dig into your plugin’s code to add a quick fix. It’s really hard to read in kakscript, and without syntax highlighting I’m kinda lost.

I’ve split my langmap.kak plugin like this:

map_mode=${1:-insert}
state=${kak_opt_langmap_toggled:-false}
# portable version of `dirname'
dir_name() {
    filename=$1
    case "$filename" in
      */*[!/]*)
        trail=${filename##*[!/]}
        filename=${filename%%"$trail"}
        dir=${filename%/*};;
      *[!/]*)
        trail=${filename##*[!/]}
        dir=".";;
      *) dir="/";;
    esac
    printf "%s\n" "$dir"
}
langmap_dir=$(dir_name $kak_opt_langmap_source)
perl $langmap_dir/../perl/langmap.pl $kak_client $map_mode $state $kak_opt_langmap_default $kak_opt_langmap | kak -p $kak_session

It’s a bit verbose, but far better compared to perl -e mess.

Yeah for the case of snippets.kak it’s a bit difficult to edit the perl code, but if you want to edit gdb.kak then you can simply do so on the files in detail/ and you’ll have syntax highlighting

Sure, I was talking about snippets.kak primarily. GDB works just fine for me, although I don’t use it often because we don’t debug at work at all.

You are a moderator! Get in there and fix it! :slight_smile:
hehe

1 Like

I don’t believe a persistent interpreter should be implemented in kak

To throw in my 2-cents: I agree, but I think it could have noticeable performance benefits, since it would eliminate startup overhead for the interpreter.
Instead (if possible), I think persistent interpreters would be modules to Kakoune. Kakoune would define available interpreter sockets, and then declare-expansion would merely ensure that a running interpreter is connected to the socket.

We need to know a path to the script, so we could call it in shell expansion

Maybe it would be helpful for Kakoune to set PWD for shell expansions.
For example: a script at ~/some/path/script.kak

echo -debug %sh{ pwd }

=> ~/some/path/

That’s kinda already solver, but a bit ugly.

If you want to use lua to script kakoune you can use a setup like the one below. I used something similar for python in kakoune-easymotion. It’s a little more complicated there because I keep the python process running to avoid the interpreter startup time.

def luadef -params 2 %{
    eval %sh{
        tmp=$(mktemp)
        echo "$2" > "$tmp"
        vars=$(grep -o 'kak_\w*' $tmp | uniq)
        echo "
            def -override $1 %{
                eval %sh{
                    # $vars
                    lua $tmp
                }
            }
            hook global KakEnd .* %{ nop %sh{ rm $tmp } }
        "
    }
}

luadef test-hello %{
    print("info 'hello from lua! session:" .. os.getenv'kak_session' .. "'")
}

Source and run the newly defined test-hello command to see the greeting.

7 Likes

Omg :open_mouth:

Adjusted your code for making Perl commands, and made it support Kakoune switches:

Sorry for screenshot, can’t copy code from mobile with comfort, but this function is available on my GitHub in my dotfiles repo if anyone interested.

2 Likes

About the naming, shouldn’t it be define-command-perl (like a switch for define-command -perl)?

IDK, define-perl-command just sounds better in my head.

Yeah I agree it sounds better; it’s like something-enable vs. enable-something somehow.

How would you define arguments for commands?
Something like this?

luadef %{test-hello -params 1 -docstring "asdf"} %{
    .... lua stuff ....
}

IMO if lots of people start doing this, we should just make native expansions for alternate interpreters (e.g. py%{}, %lua{}, %perl{}, etc.). After all, luadef test-hello %{...} is just a work-around for doing def test-hello %lua{...}. In general, my stance is that work-arounds are for edge cases, if those cases gain popularity they should be implemented properly as features.