Julia and kitty terminal integration

Hello all!

This is not a question, rather sharing a solution I couldn’t find readily available.

I recently started using the kitty terminal, and I like it a lot (this is not an official endorsement). One of the many great features it has is shell integration, which allows you to, e.g., easily navigate between the previous prompts using your keyboard, and to feed the output of any of the previous commands into a pager. Very-very useful when you are faced with a multipage output and realize that you should have put a “| less” on the end of your pipeline. Furthermore, kitty is fully configurable and extensible. Among others, this shell integration feature can be added to shells which aren’t supported out of the box. For example, Julia!

Here’s what to do to make kitty recognize your Julia REPL prompt. Put the following code in your startup.jl (or merge it with your own startup code, if you already have some):

using OhMyREPL
const iskitty = ENV["TERM"] == "xterm-kitty"
OhMyREPL.input_prompt!(:green) do
    iskitty && print("\e]133;A\e\\")
    return "julia> "
OhMyREPL.output_prompt!(:red) do
    iskitty && print("\e]133;C\e\\")
    return ""

And that’s it! You can now use kitty shortcuts ctrl+shift+z and ctrl+shift+x to jump back and forth between previous prompts, ctrl+shift+g to put the result of the last command in a pager, and ctrl+shift+right click on any of the previous outputs to do the same with that. (Assuming, of course, that you have kept the default keybindings.)

Note that the control sequences must be printed directly rather than returned as part of the prompt string. If you did the latter, the REPL would count those characters as part of the prompt, and would position the cursor incorrectly (effectively leaving a large horizontal gap between the cursor and the characters being typed).

Disclaimer: the ctrl+shift+g command works only if your last command produced a value and that was printed on the REPL output. It does not capture printed outputs, sadly. The mouse click trick, on the other hand, works for all sorts of output.

There you go! Hope you like it.


The problem I have with ENV["TERM"] == "xterm-kitty" is that it doesn’t work when over SSH TERM is changed to xterm-256color. So, I use this:

import REPL
const TERM_KITTY = isinteractive() && let
    REPL.Terminals.raw!(REPL.TerminalMenus.terminal, true)
    output = @task readuntil(stdin, "\e\\")
    Timer(0.05) do _
        istaskdone(output) || Base.throwto(output, InterruptException())
    value = try
    catch _
        return false
    REPL.Terminals.raw!(REPL.TerminalMenus.terminal, false)
    termcode = String(hex2bytes(last(split(value, '='))))
    termcode == "xterm-kitty"

Sure, whatever floats your boat. I’m not familiar with that specific termcode you used, and to me, this seems too much of a hassle, but hey, varietas delectat.

Side note: the creator of kitty leaves no doubt about what he thinks of using term types other than xterm-kitty within kitty. He also provides a wrapper around ssh using kitty +kitten ssh, which ensures that the xterm-kitty terminfo file is readily available on the other side of the SSH connection as well. You can find more info in the kitty FAQ and in various GitHub issues.

Does not seem to support windows.

1 Like

Yes, kitty itself doesn’t support Windows (yet?), it runs on Posix systems. That being said, it would probably work in WSL, although I haven’t tried.