Open file in same directory / tab-expand %val{...}

Hi,

is there a simple way to quickly navigate to the dirname of the current buffer, e.g. to open another file in the same folder? I found %val{buffile}, but TAB doesn’t seem to expand it.

Essentially something like vim’s %:h (which was already too verbose, but at least does TAB-complete).

<c-r>% pastes the current buffer name.
<a-!> expands things like %val{buffile}
also works with shell blocks, for example
map global user e ':e %sh{dirname $kak_buffile}<a-!>/'

3 Likes

Thanks, that’s really helpful. I’ve also defined

define-command -docstring 'cd to where current buffer lives' cdhere 'cd %sh{dirname $kak_buffile}'

I assume I don’t have to worry about quoting / ending parameters, as one has to in bash (cd -- "$dir")?

You do need to quote the argument to cd in case it has spaces but everything “inside” a %{} block is treated as a single string. So %sh{} or %opt{} can have spaces in the result and it won’t get interpreted as different arguments, unlike $() in the shell. In other words what you need to handle any whitespace in the path is

'cd %sh{dirname "$kak_buffile"}'
or alternatively
'cd %sh{dirname $kak_quoted_buffile}'

Just to be clear, those two variants aren’t equivalent.

From dash(1), the expansions the shell performs are:

  1. $-based and ~-based expansions, all in a single pass
    • tilde expansion: ~/ replaced with user’s home directory, etc.
    • parameter expansion: $FOO, ${BAR}, etc.
    • command substitution: $(foo), `bar`
    • arithmetic expansion: $(( 1 + $FOO ))
  2. Field splitting
    • except for parameters expanded inside double quotes
  3. Pathname expansion (globbing)
    • except for parameters expanded inside double quotes
  4. Quote removal
    • except for quotes introduced by expansions

In the first example, we have:

  • An initial string:
    • dirname "$kak_buffile"
  • parameter expansion:
    • dirname "/path/to/some file.txt"
  • field splitting:
    • dirname
    • "/path/to/some file.txt"
  • no globs to expand
  • quote removal:
    • dirname
    • /path/to/some file.txt

In the second example, we have:

  • An initial string:
    • dirname $kak_quoted_buffile
  • Parameter expansion:
    • dirname '/path/to/some file.txt'
  • field splitting:
    • dirname
    • '/path/to/some
    • file.txt'
  • no globs to expand
  • no quote removal, since all quotes were introduced by parameter expansion

You can tweak the second example to behave if you do something like:

eval "dirname $kak_quoted_buffile"

…which results in:

  • parameter expansion:
    • eval "dirname '/path/to/some file.txt'"
  • field splitting:
    • eval
    • "dirname '/path/to/some file.txt'"
  • no globs to expand
  • quote removal
    • eval
    • dirname '/path/to/some file.txt'
  • eval takes the given string and repeats the process from the beginning:
    • dirname '/path/to/some file.txt'
  • no parameters to expand
  • field splitting:
    • dirname
    • '/path/to/some file.txt'
  • no globs to expand
  • quote removal:
    • dirname
    • /path/to/some file.txt

That’s the result we want, but it would have been simpler just to use $kak_buffile inside double-quotes to begin with.

5 Likes

Interesting, I’ve never run into the consequences of that distinction before. Like any sane person I just wrap everything in double quotes. I guess the behaviour is to prevent quotes in variables from messing with the “top level” quoting.

Very interesting, thanks. On this quoting note, I’m trying to make kak source a script whose name is stored in environment variable FNAME. I figured

source %sh{printf %s "$FNAME"}

would be the definitive way to get back the filename into kak. There’s nothing like %env{} AFAICT. Correct?

That’s simple and will definitely work.

It gets complex because Kakoune has a client-server architecture, and each client (and the server) can have a different set of environment variables, a different current directory, etc. If you wanted to use the current client’s environment to source a script, you could use source %val{client_env_FNAME} but since sourcing a script can affect the entire session, using the server’s environment is probably the right thing to do.

1 Like