Vim like autochdir, rest arguments, returning things from eval %sh{}, highlight all numbers

Hey again, everyone.

So, 3 more questions for y’all

  1. How to implement Vim’s autochdir feature.

    Something nice I had found in Vim was this feature autochdir, which simple just cd's into the directory that the opened file is in. This is separated for different files, which is nice.

    Is this possible in Kakoune? I do have this command written up

    define-command autocd -docstring %{
     Command to change the directory to the
     location of the currently opened file.
     } %{
     nop %sh{
         if [ "$PWD" != $(dirname $kak_buffile) ]; then
             cd "$(dirname $kak_buffile)" || return
         else
             return    
         fi
     }    
    }
    

    But having this run on BufOpenFile or WinCreate doesn’t do anything (and neither does having evaluate-commands instead of nop).

  2. Rest arguments for commands

    I know especially in Common Lisp, functions can have “rest” arguments which would look like

    (defun some-function (arg1 &rest args)
       ;; body in here
    )
    

    And args would represent some variable length list.

    %arg{} in Kakoune is either n or it’s @, which we all know %arg{@} is the same as $@ in shell script.

    So in my command that I have, called rlwrap I do this

    evaluate-commands %sh{
        cmd="$1"
        shift
        args="$@"
    }
    

    which feels hacky however it’s seems to be the simplest option because POSIX Shell doesn’t have arrays like Bash does. If this were Bash, you could do "${@: 2}".

    Is this really the only option at the moment?

  3. Return values from eval %sh{}

    Is it at all possible to pass values that were made in a %sh{} block and pass them back in Kakoune code?

    So say

    %{
      # some Kakoune stuff here
      %sh{
          var=$something         
      }
      # we then do something with var here
    }
    

    would return or export allow for this?

  4. Highlighter for only numbers

    I know Screwtape gave a little tutorial and there’s this regex in that: (\+|-)?[0-9]+(\.[0-9]+)?.

    But well,

    1. I’m an idiot, and I had it working but now it isn’t

      I can’t remember what I did to have this work, but this is what I have now

      face global Numbers ${brmagenta},${bg}+b
      add-highlighter shared/numbers regex (\+|-)?[0-9]+(\.[0-9]+) 0:Numbers
      
    2. This doesn’t highlight what I wish it would, and I’m awful at regex.

      I would like to highlight digit, digits with decimals, percentages, and not numbers next to or surround by characters. Again, I’m terrible at remember everything about regex so I wouldn’t exactly know how to do that.

Thanks, everyone.

That’s because kakoune spawns a new shell process. Kakoune has a builtin cd (change-directory) command. To do what you want, you can write

cd %sh{dirname $kak_buffile}

Why does that work? Because %sh is a string subtitution. The output of the shell is used as a string. It’s very useful to execute different things depending on shell logic. Something like:

execute-keys %sh{
    if ...
        echo xx
    else
        echo dw
    fi
}

That answers question three as well.

Can you state exactly what you want? If you are not seeing anything highlighted, it may be because of two reasons.

Firstly, you are adding a shared highlighter. Those aren’t used, they are stored because compiling a regular expression takes a bit of time, so you can add them there if you plan on activating and deactivating it without recompiling it.

Secondly, unless you only want to highlight decimal numbers, you should add a question mark at the end, indicating that the last chunk inside the parenthesis, which is the expression for the decimal part, is optional.

And, if you don’t want to highlight numbers inside words, you can add \b where you want to match a word boundary.

Maybe you want something like this:

add-highlighter global/numbers regex \b(\+|-)?[0-9]+(\.[0-9]+)?\b 0:Numbers
2 Likes

You can set an option or a register from inside the shell scope if you use it with evaluate-commands, e.g.

declare-option str return_value
eval %sh{ printf 'set-option window return_value "%s"\n' "$something" }
# or
eval %sh{ printf 'set-register x "%s"\n' "$something" }

Ah so could even execute commands as well from %sh? Like %sh{printf %s "command args"}?

Well say numbers from 0…[some really big number here], decimals (like 9.13234323 etc.), and percentages. Maybe fractions/rations, but the more to highlight the more complex it is to get a regex to match all cases.

Ah yes I didn’t even think of registers!

There’s a reason you might not want this: in Vim, everything happens in one OS window, so Vim knows exactly which buffer has focus and can reliably change directories. On the other hand, Kakoune has multiple clients in separate windows, and isn’t always notified when focus changes from one to another. You could even have different clients connected to the same Kakoune session running in different tmux sessions, or different X11 sessions, so multiple clients could legitimately have focus at the same time. In that scenario, which buffer’s directory should be the current directory?

If you’re sure you’re never going to have multiple clients in different sessions, and your terminal emulator supports focus events, it should probably work as far as I know.

Speaking of which, rather than hooking BufOpenFile or WinCreate, I suggest hooking WinDisplay (which fires when a client switches to a particular buffer) and FocusIn (which fires when you switch clients, modulo the exceptions I mentioned above).

POSIX shell does have arrays; or at least, it has one array, the argument list. You probably don’t ever want to say args="$@" though, because it squashes all the arguments into a single string, instead of keeping them separate.

As @useredsa mentioned, the most direct problem is that you’re creating the highlighter in the shared/ namespace, ready and waiting in the wings, but never actually brought on stage. If you want it to be everywhere, you can create it in the global/ namespace as per their example, or if you just want it in a certain window you can do something like:

add-highlighter window/numbers ref numbers 

…which is a little bit like a symlink in window/numbers pointing to shared/numbers.

That wouldn’t work as well as you’d hope; %sh{} expands to a single string, so if you did something like:

%sh{echo info hello}

…then Kakoune complains there’s no such command as info hello, rather than executing the info command with a single argument. That’s why we use eval %sh{} instead of %sh{} directly: eval takes the single string produced by %sh{} and breaks it back up into individual words Kakoune can parse.

If you do want a single string, though, %sh{} is great. Your question about getting values back out of a shell block could easily be done by setting a register or an option to a shell-block value:

set-register a %sh{echo hello world}
set-option window tabstop %sh{detect-tab-size "$kak_buffile"}

(somebody should write a detect-tab-size tool, it’d be pretty handy)

1 Like

Ah I see what you mean. I would probably end up having to specify my autocd command in more commands then I like or chain is with other commands constantly for it to work. Sounds like a bit of a pain.

That’s $*. $@ keeps them separate. $* is the one that squashes them.

Sorry the example in my replay to useredsa I meant to imply eval[uate-commands].

Ok, starting with the previous expression.

\b                      word boundary
(\+|-)?                 sign
[0-9]+                  whole part
(\.[0-9]+)?             fractional part
(/[0-9]+(\.[0-9]+)?)?   denominator
( ?%)?                  percentage with optional space
\b                      word boundary
1 Like

Awesome! How would I put that all together though?
\b(\+|-)?[0-9]+(\.[0-9]+)?(/[0-9]+(\.[0-9]+)?)( ?%)?\b give me this

'add-highlighter' regex parse error: unclosed parenthesis at '\b(\+|-)?[0-9]+(\.[0-9]+)?(/[0-9]+(\.[0-9]+)?)(<<<HERE>>>'

So it’s an issue with ( ?%)?. Is a \ needed?

You need to quote the string because it contains an space. Otherwise, it would be treated as 2 arguments instead of one. You should use single quotes (') because those prevent expansions. If you do use double quotes ("), then you can escape a percentage with another percentage, as stated in doc command-parsing.

By the way, because percentages do not count as word characters, they were not being highlighted :confused:. Fortunately, you can specify that you either want a word boundary or a percentage.

Here’s the final regex:

'\b(\+|-)?[0-9]+(\.[0-9]+)?(/[0-9]+(\.[0-9]+)?)?( ?%|\b)'
1 Like

Thanks, bro!

1 Like

If you say somecmd "$*" then all the arguments get squashed into a single string.

If you say somecmd "$@" then the individual arguments are preserved.

However, that’s only in the context of a command. A command has a list of arguments, so "$@" lets you splice the array of arguments you were given into the array of arguments you provide to some other thing.

Variables are different. In plain POSIX sh, you can’t have an array variable, a variable must be a single string. If you say args="$*" then all the arguments will be squashed into a single string, but if you say args="$@" then all the arguments will still be squashed into a single string, because there’s nowhere else for them to go.