# REPLConnect.jl

**URL:** <https://discourse.julialang.org/t/replconnect-jl/140012>\
**Category:** Tooling\
**Tags:** neovim\
**Created:** [October 14, 2026, 2:59am UTC](https://discourse.julialang.org/t/replconnect-jl/140012 "2026-10-14T02:59:03Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![arnold-c](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/arnold-c/32/222741_2.png) [@arnold-c](https://discourse.julialang.org/u/arnold-c)\
**Post date:** [October 14, 2026, 2:59am UTC](https://discourse.julialang.org/t/replconnect-jl/140012/1 "2026-10-14T02:59:03Z")

</div>

**TLWR:** I have a Neovim plugin and small Julia package that connects a Julia REPL to a Neovim session, allowing for a variable explorer pane and viewer, as well as a plot pane that works over SSH, and object autocompletion based on objects that are loaded in the REPL.

Small announcement of a Neovim plugin and associated Julia package I’ve been playing around with for the last couple of weeks to help aid my Julia development of data-heavy analysis. There are a couple of things that I’ve missed about IDE-based development since switching to Neovim about 3 years, and most have been resolved with improvements to the language server(s) and testing environment. However, the main pain point that stuck around (apart from debugging) was the RStudio/Positron-style variable viewer and plotting pane. When Posit announced Ark kernel and lsp a few years ago I was interested to see if it would ever be extended to work in Neovim, and about a year or so of work Jacob released `jet.nvim` that provides an interface to do this through language-specific extension (e.g., `jet.ark`). I got to playing around with an implementation for the IJulia kernel, that you can see at [`jet.julia`](https://github.com/arnold-c/jet.julia). It works and provides some useful features like the object autocompletion and variable pane/viewer. But I much prefer working in the actual Julia REPL over the IJulia kernel, so I wondered if we could achieve a similar outcome using Unix sockets to pass information to and from the REPL.

[`REPLConnect.nvim`](https://github.com/arnold-c/REPLConnect.nvim) and [`REPLConnect.jl`](https://github.com/arnold-c/REPLConnect.jl) are the outcome of this experimentation (with a lot of the JSON-RPC approached based on the work by [positron-julia](https://github.com/TidierOrg/positron-julia)).

The general idea is that the Neovim plugin handles all the core functionality, and each language has it’s own Neovim plugin to hook into the core one, and a language-specific package to handle things from the REPL’s end of things. I have added a small function call to my `startup.jl` file that activates it for each interactive REPL session and does things like track REPL entry history (only the last 10 by default) so you can detach and reconnect to a different running REPL in the situation where you have a couple running in parallel, or “capture” plots so they are sent to the internal plot viewer that opens in Neovim and can work over SSH (this hooks into `snacks.nvim` and assumes you have a terminal that uses the Kitty image protocol).

```julia
# in my startup.jl
if isinteractive()
    # you likely want to disable VimBindings for a nvim
    # terminal if you use VimBindings
    if !haskey(ENV, "NVIM")
        using VimBindings
    end

    using REPLConnect: REPLConnect
    REPLConnect.start()
    REPLConnect.capture_plots!(true)
    REPLConnect.record_history!(true)
end

```

If you prefer to use REPLs created outside of Neovim and send code using something like `vim-slime`, that’s no problem, but you can also create Neovim splits and terminals and connect to those if you prefer. The choice is yours. The thing about this plugin that I’m (probably unreasonably) pleased with is that it’s possible to define default layouts in your neovim config so the various panes that open/close as you need them do so in a pre-specified way, and you can set them differently for external REPLs _vs_ vertical nvim terminals _vs_ horizontal nvim terminals. _And_, there’s even a function to “capture” the current layout allowing you to manually adjust the sizing to your preferences and then bake it into your Neovim config!

> **Show my neovim plugin config**
>
> ```lua
> return {
> {
> 'arnold-c/REPLConnect.jl',
> dependencies = { 'arnold-c/REPLConnect.nvim' },
> main = 'replconnect.julia',
> opts = { command = { 'julia', '--project=.' } },
> },
> {
> 'arnold-c/REPLConnect.R',
> dependencies = { 'arnold-c/REPLConnect.nvim' },
> main = 'replconnect.r',
> opts = { command = { 'arf' } },
> },
> {
> 'arnold-c/REPLConnect.nvim',
> main = 'replconnect',
> ft = { 'r', 'julia' },
> 
> -- The :REPL* commands are filetype specific, so no per-language routing is needed.
> -- NOTE: only :REPLStart is confirmed; verify the other command names.
> keys = {
> { '<leader>ra', '<cmd>REPLAttach<cr>', desc = 'REPL: attach to external REPL' },
> { '<leader>rd', '<cmd>REPLDetach<cr>', desc = 'REPL: detach from REPL' },
> { '<leader>rs', '<cmd>REPLStart<cr>', desc = 'REPL: start REPL with default split' },
> { '<leader>rS', '<cmd>REPLStop<cr>', desc = 'REPL: stop REPL(s) within the project directory' },
> { '<leader>rl', '<cmd>REPLLayout<cr>', desc = 'REPL: save current layout to clipboard' },
> { '<leader>rp', '<cmd>REPLPlots<cr>', desc = 'REPL: toggle plots pane' },
> { '<leader>rv', '<cmd>REPLVariables<cr>', desc = 'REPL: toggle variables pane' },
> },
> 
> opts = {
> -- exclude items from appearing in the variable pane e.g., default templates loaded in your startup.jl
> exclude = {
> 'default_dir',
> 'default_templates',
> 'default_user',
> 'home_dir',
> 'in_global_env',
> 'installed_deps',
> 'inplace_template',
> 'internal_template_dir',
> 'optional_dependencies',
> 'personal_templates',
> 'required_dependencies',
> 'specified_activate_project',
> },
> completion = true, -- runtime completion LSP in buffers of attached REPLs' languages
> poll_interval_ms = 1500, -- refresh the visible variables pane; 0 = off
> update_while_busy = false, -- fetch changes while the REPL evaluates
> repl = { -- :REPLStart; these apply to every language
> split = 'vertical', -- or "horizontal"
> enter = true, -- start in terminal mode
> capture_plots = true,
> plot_limit = 50,
> },
> layout = {
> -- One layout string per way the REPL is shown; see "Pane layouts" below.
> horizontal = [[editor / repl @ 36% | ((plots \ view) @ 60% / vars) @ 35%]],
> vertical = [[repl @ 22% | editor | ((plots \ view) @ 62% / vars) @ 35%]],
> external = [[editor | ((plots \ view) @ 56% / vars) @ 40%]],
> },
> plots = {
> renderer = 'auto', -- "snacks" | "image.nvim" | "external" | custom table
> remote = 'auto', -- stream images via Snacks: "auto" (over SSH, if snacks is loaded) | true | false
> auto_open = true,
> keymaps = {
> close = 'q',
> next = '<C-n>',
> previous = '<C-p>',
> refresh = 'r',
> follow = 'f',
> external = 'o',
> save = 's',
> },
> },
> viewer = {
> keymaps = {
> close = 'q',
> refresh = 'r',
> next_page = ']p',
> previous_page = '[p',
> scroll_right = '<Tab>',
> scroll_left = '<S-Tab>',
> next_slice = { '<C-n>', ']s' },
> previous_slice = { '<C-p>', '[s' },
> select_slice = '<LeftMouse>',
> },
> },
> },
> config = function(_, opts)
> require('replconnect').setup(opts)
> 
> -- On the first Julia/R file of a session, offer to attach to running REPLs of
> -- that language. A lone session in the cwd is attached automatically;
> -- anything else (several, or none in the cwd) brings up the picker.
> local discover = require 'replconnect.discover'
> local languages = { julia = 'julia', r = 'r' } -- filetype -> REPLConnect language
> local prompted = {}
> 
> local function connect(filetype)
> local language = languages[filetype]
> if not language or prompted[filetype] then
> return
> end
> prompted[filetype] = true
> 
> local cwd = vim.fn.getcwd()
> local sessions = vim.tbl_filter(function(info)
> return discover.language(info) == language
> end, discover.list { cwd = cwd })
> if #sessions == 0 then
> return
> end
> 
> local here = vim.tbl_filter(function(info)
> return info.cwd == cwd
> end, sessions)
> vim.cmd('REPLAttach' .. (#here == 1 and (' ' .. here[1].pid) or ''))
> end
> 
> vim.api.nvim_create_autocmd('FileType', {
> group = vim.api.nvim_create_augroup('replconnect.connect-first-file', { clear = true }),
> pattern = vim.tbl_keys(languages),
> callback = function(args)
> vim.schedule(function()
> connect(vim.bo[args.buf].filetype)
> end)
> end,
> })
> 
> -- Lazy-loaded on a filetype: the triggering buffer's FileType already fired.
> connect(vim.bo.filetype)
> end,
> },
> }
> 
> ```

> **My \`blink.cmp\` sources options to prefer showing REPLConnect.nvim suggestions over other enabled LSPs**
>
> ```lua
> sources = {
> default = {
> 'lsp',
> 'path',
> 'snippets',
> 'buffer',
> },
> providers = {
> lsp = {
> -- Boost items from Jet and REPLConnect clients above other LSP clients.
> transform_items = function(_, items)
> for _, item in ipairs(items) do
> local client = vim.lsp.get_client_by_id(item.client_id)
> if client and (client.name:match '^replconnect') then
> item.score_offset = (item.score_offset or 0) + 100
> end
> end
> return items
> end,
> },
> },
> },
> },
> opts_extend = { 'sources.default' },
> 
> ```

One other neat feature is the variable viewer (that can be accessed by pressing `v` on a variable in the general variable pane) not only works for dataframes and other Tables.jl objects, but also for 3D arrays _and_ for function methods. This last one allows you to quickly view all the method definitions for a function, which is pretty handy with generics in R and multiple dispatch in Julia, and also comes with a go-to-definition `gd` keymap that brings you to the file location where the function is defined (using a snacks picker to choose the correct method). It is set up to use the language server, but right now seems to always trigger the fallback method that searches the projects’ files manually. Either way, it seems to be working out OK in my day-to-day usage, but I’m working to figure out whether the issue is on my end (likely) or in how capabilities are advertised by the [`fatou` lsp](https://github.com/jolars/fatou) (unlikely).

It’s definitely an early-beta type set of packages and will probably experience some (maybe breaking) changes over the coming months, but I’ve been daily driving it with Julia and R (via the [`arf`](https://github.com/eitsupi/arf) REPL) for the last 1.5-2 weeks and it’s been pretty reliable.

And final obligatory disclosure: yes, I did use AI to help write the bulk of this and iterate on a number of different architectures. I think the code is pretty reasonable as I’ve been going through and reviewing it along each step of the process, but you may prefer not to use it because of AI - that’s fine! I just wanted to share in case others find it useful.

---

<div class="post-metadata">

**Author:** ![Qfl3x](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/qfl3x/32/224645_2.png) [@Qfl3x](https://discourse.julialang.org/u/Qfl3x)\
**Post date:** [October 14, 2026, 8:13am UTC](https://discourse.julialang.org/t/replconnect-jl/140012/2 "2026-10-14T08:13:28Z")

</div>

I’ve been a long-time user of REPLSmuggler, and definitely prefer to have Julia separate from nvim itself, I do stuff that may cause Julia to crash or where Julia has to run in a remote server. Might try this out this weekend!
