Opening Command Output in a New Buffer
Tags: kakoune, unix, postgres
Published: 2020-10-05 21:29:57

[Last time](https://dev.to/staab/running-queries-in-kakoune-with-a-custom-command-4741), we tweaked our [kakoune query browser](https://dev.to/staab/using-kakoune-as-a-query-browser-2j72) to open the selected query in an info window. As I mentioned at the bottom of that post, there are a few problems with this over-simplistic approach, one of which is the limitations of the info window.

Today I'll be tweaking the plugin by moving the query output into its own scratch buffer, which we'll open for the user when a query is run. As an added bonus, I'll be generalizing the plugin to work the same for any external command, not just databases.

To start with, let's review our plugin:

```
def eval-query \
  -override \
  -docstring "Evaluate current selection using given connection." \
  -params 1 \
  -shell-script-candidates %{
    cat .eval-query.json \
      | jq -r '.connections|keys|@sh' \
      | sed s/\'//g \
      | tr -s " " "\n"
  } \
%{
  info -title "Query output" %sh{
    psql `cat .eval-query.json | jq .connections.$1 | cut -d\" -f 2` \
      -c "${kak_selection}" 2>&1
  }
}
```

The salient information here is that `info` command. Instead of popping up an info dialog, we're going to capture the output and put it in the `|` register using `set-register`. Below is the modified command body:

```
set-register | %sh{
  cmd=`cat .eval-query.json | jq .connections.$1 | cut -d\" -f 2`
  eval "$cmd "${kak_selection}" 2>&1"
}
```

We used the `|` register, since `:doc registers` states that it should be used by commands that spawn a subshell. I'm not sure if this is 100% kosher, so leave a comment if you know better!

Next, we're going to open a new scratch buffer called *eval-query-output* and stick the contents of the register in there. This is pretty straightforward:

```
edit -scratch *eval-query-output*
exec 'geA<ret><esc>"|p;'
```

The `edit` command is probably familiar; it just opens a new buffer. `-scratch` tells kakoune we don't care about linking it with a file. Finally, we use `execute-keys` (`exec` for short) to go to the end of the file, add a new line, and paste the contents of the `|` register.

And that's it! See it in action below:

![Opening query results in a new buffer](https://dev-to-uploads.s3.amazonaws.com/i/8fzh5uwqfs8n7js3yij3.gif)

Because the mechanism for running a query is so generic, this plugin can be used just as easily to open anything in a new buffer, not limited to query results. By simply renaming `connections` to `commands` and updating our config file (I also renamed the plugin to `beval` for "buffer eval"), the possibilities become endless!

Let's add a fancy `ls` command that lets us open a directory listing in a new buffer:

```
{
  "commands": {
    "ls": "ls -lah"
  }
}
```

![Opening a directory listing in a new buffer](https://dev-to-uploads.s3.amazonaws.com/i/83rap7doa7rpkb3dx6tp.gif)

Finally, we can cheat by adding an `eval` key to run any command we want:

```
{
  "commands": {
    "eval": "eval"
  }
}
```

![Evaluate an arbitrary command](https://dev-to-uploads.s3.amazonaws.com/i/79ss0o7960jixg70mzxe.gif)

What we basically have now is `|` on steroids (and less well-designed). Let's go ahead and refactor this to split out the "pipe to buffer" functionality as a standalone thing.

```
def eval-to-buffer \
  -override \
  -docstring "Evaluate current selection into scratch buffer." \
  -params 1..1000 \
%{
  set-register | %sh{ eval "$@ 2>&1" }

  # Open our output buffer
  edit -scratch *command-output*

  # Put output into the buffer
  exec '%"|pd'
}

def beval \
  -override \
  -docstring "Evaluate current selection using named command into scratch buffer." \
  -params 1 \
  -shell-script-candidates %{ cat .beval.json | jq -r '.commands|keys|@sh' | sed s/\'//g | tr -s " " "\n" } \
%{
  eval-to-buffer %sh{
    cmd=`cat .beval.json | jq .commands.$1 | cut -d\" -f 2`
    echo "$cmd \"${kak_selection}\""
  }
}
```

Now our project-specific configuration is independent of our mechanism for piping output to a buffer, since both are useful in different circumstances.

Further refinement is of course out there, but for now, I'll leave this here. Thanks for reading!