Slime Without Plugins Using Kitty
What are slime plugins?
We speak here of plugins for text editors. These are pieces of code that some editors can incorporate to endow them with extra powers. One class of these, designed to create intimacy between an editor and a repl running in another window, are often called slime plugins. There are dozens of them.
The idea is this: after enhancing your Emacs, Neovim, or other editor with one of these plugins, you can start writing or editing code as usual. If you’re writing your project in a language with an interactive prompt, or repl, you can open another window in your terminal emulator (or within your editor if it has that feature) and start the language interpreter. Many languages have repls: Julia, Python, Lua, gnuplot, and, by definition, any shell. Even Fortran, traditionally a non-interactive compiled language, has dipped its toe into the interactive sea.
The basic functionality that all slime plugins provide is a connection between your editor and the repl that allows you to send pieces of code from the former to the latter, where these fragments will be executed. Various plugins offer additional conveniences, but this transmitting of text is the core feature that I’m talking about today.
Of course you can manually copy the code, switch to the repl, and paste: you don’t need any plugins for that. You can also just work in the repl without using an editor. But writing in an editor using a slime plugin is usually better. For one thing, the file you’re editing is a permanent record of your work. The good ideas that you hit on while working directly in the repl are more ephemeral, even if you’re using a repl such as Julia’s, which stores your repl session in a file. Slime plugins let you evaluate code in the editor with a single keyboard shortcut, which is much faster and more fluid way of working than the manual, multi-step process. The slime way encourages you to try out lines of code while you’re writing them, leaving you with a program that remains bug-free as it evolves.
Slime Without a Plugin
The problem with slime plugins is that they are plugins. There may not be one that you like for your editor. After you install one, it only works for that editor. Like all plugins, it may interact with other plugins in ways you don’t want. If it works today, it may not work tomorrow after you update your editor, the plugin, or other plugins.
If you happen to use the Kitty terminal emulator, you can get slime functionality without having to depend on plugins. The method relies on Kitty’s built-in remote control abilities and works across all editors and repls. It can even be used to send text between windows where no editor is running.
If you happen to be using a different terminal emulator and you spend a lot of time in the terminal, I suggest you look into Kitty to judge whether you might benefit from its features. There’s a lot more to Kitty than what I describe here.
Using Kitty features to replace slime plugins is dead simple. Kitty has the ability to send text from any of its windows to any other. Our goal is to be able to send any selected text in one window to the repl, or more generally to any other window, with a keyboard shortcut.
We begin by adding this keyboard binding to the Kitty configuration file, which is normally .config/kitty/kitty.conf in your home directory:
map kitty_mod+; remote_control_script /usr/local/bin/sel2recent
The kitty_mod is by default shift-control, but can be changed if you don’t like that. The above line executes the program sel2recent when you press shift, control, and the semicolon simultaneously. After changing the configuration file you can either restart Kitty or (by default) hit kitty_mod+f5 to reload the configuration in all existing Kitty windows (recent Kitty versions will automatically reload the configuration after a short (configurable) interval.)
Next we create the invoked program. The contents of sel2recent are:
#! /bin/zsh
kitten @ send-text --match "recent:1" "`xclip -o`\r"
I happen to use zsh, but you can of course invoke any shell in the first line.
The kitten @ send-text command is the remote control program that ships with Kitty. It does what it says: sends text somewhere. The destination is given by --match "recent:1", which sends the text to the most recently active Kitty window. The final argument to send-text is the text to send. We want to send whatever is selected in the window we’re working in, which we can get from xclip as shown. If you don’t have xclip already installed, you can get it from your package manager. xclip -o outputs the selected text, which Linux automatically makes available (the “primary selection”) without having to perform a “copy” step. In MacOS you’ll have to make an adjustment here; I don’t think it has a primary selection buffer.
How To Use: Sessions
I lied a bit up there when I said that the send-text command will send the text to the “most recently active Kitty window”. It will do so, but only to windows that are contained within the current Kitty tab. You can also send text to other OS-level Kitty windows, using sockets (for example), but that’s a story for another day.
The slime replacement described here is intended to work within a Kitty session, which is a collection of Kitty terminal windows contained within a single tab. As I don’t use Kitty tabs, for me that just means a collection of Kitty windows within an OS-level window (the windows that you manipulate using your window manager.)
To create a session, just open a Kitty terminal window (how to do this is left as an exercise for the reader) and then split it into as many other windows as desired, using the shortcuts defined in your configuration file. In mine I have
map kitty_mod+/ launch --location hsplit
map kitty_mod+. launch --location vsplit
which lets me split any window horizontally or vertically. Then you can adjust the layout of the sub-windows using keyboard commands or the mouse.
You can start programs in some or all of the windows (which might be better called “panels”) and then save the whole thing as a session by pressing f1. You will prompted for a session name. Later you can use the command
kitty --session <path-to-session-file>
to load the session, which will have all your panels configured the way they were when you saved it, with the same programs running.
The video below shows an example of the kind of session that we might want to use with our slime setup. It has Neovim running in the top panel, and, below that, two smaller panels. The left one is running a Julia repl the one next to it a Lua repl. The sel2recent script sends the selected text to whichever panel that was last in focus before the one currently in focus. So to select a target for send-text, just focus it, either with the mouse or, preferably, by switching to the desired target panel using the Kitty shortcut kitty_mod+] or kitty_mod+[ and switch back to the source panel. In a typical programming session you’ll be sending text from the editor to one repl, so you won’t have to do this target selection often, but the video shows the flexibility of the setup. (If you were using this kind of setup regularly you could define Kitty shortcuts to send text to the Julia or Lua panel without having to designate targets first. The target flag on send-text can specify panels by title, the program running within them, or many other criteria.)
Better Neovim Integration
What we have so far allows us to select text and hit a shortcut to send it to another panel. But this isn’t good enough, because we don’t want to have to select text with the mouse. The video below illustrates a way of working that uses the keyboard only. Text is selected with the usual Vim visual selection, which is automatically copied into Linux’s primary selection (this has benefits aside from slime functionality).
To enable this, place this bit of Lua code in your Neovim configuration:
vim.api.nvim_create_autocmd('CursorMoved', {
desc = 'Keep * synced with selection',
callback = function()
local mode = vim.fn.mode(false)
if mode == 'v' or mode == 'V' or mode == '\22' then
vim.fn.setreg('*', vim.fn.getregion(vim.fn.getpos('.'),
vim.fn.getpos('v'), { type = mode }))
end
end,
})
I stole this from this discussion. This is the final piece that makes the slime-with-Kitty method a fluid, keyboard-only, efficient and fun way to work.
In the video we’re editing a document that compares Julia and Lua syntax. First we send the definition of a matrix to the Julia repl, and then send an assertion to Julia to check that it agrees. We then do the corresponding think with Lua, sending the definition and assertion to the repl running in the lower-right panel.

