Liking cljdoc? Tell your friends :D

Carto Flow for Vim

The hive.carto-flow.vim IAddon, MIT. It is the Vim vessel extension of hive.carto-flow: it registers the presenter :vim on the core timeline and shows every Carto operation frame in a Vim buffer.

The addon runs hive-vessel's :vim-channel executor on 127.0.0.1, which speaks Vim's JSON channel protocol (:help channel-commands). Each frame reaches Vim as ["call", "carto_flow#ingest", [message]], and the message already carries the rendered timeline line and detail lines, so Vim only lays them out. A Vim that connects late first receives the whole timeline. The executor holds one Vim connection at a time: a newly connected Vim replaces the previous one.

The listening port is written to $XDG_STATE_HOME/hive/carto-flow/vim.port (~/.local/state/... when XDG_STATE_HOME is unset) and removed on shutdown. Set :carto-flow.vim/port in the addon config for a fixed port; the default 0 picks a free one.

Vim setup

Needs Vim 8.2 or later built with +channel, +timers and +packages.

Automatic, at injection

The manifest declares an :addon/runtime (hive-addon's client-runtime seam). When the host mounts the addon with a runtime provisioner (hive-addon.runtime.boundary/provisioner passed to mount! as :provision), injection installs the plugin into Vim's package path, ~/.vim/pack/hive/start/hive-carto-flow, with a generated loader. Every Vim started afterwards loads it and, once it has entered, connects to this addon's port file and opens the timeline on the first live change. A Vim that is already running is activated through the provisioner's :eval-fn when the host supplies one. Nothing has to be typed. Tearing the addon down with the matching :deprovision removes the install.

Manual

The addon also extracts its plugin to a stable directory, by default ~/.local/state/hive/carto-flow/vim-plugin (the :carto-flow.vim/plugin-dir hook returns the exact path). Add it to your runtime path:

set rtp+=~/.local/state/hive/carto-flow/vim-plugin
syntax on

Then run :CartoFlow, or set g:carto_flow_autoconnect = 1 to connect on VimEnter. It connects using the port file and reconnects on its own when the server restarts.

Using the timeline

Live frames follow the edit: the changed file opens at the first changed line of the frame's diff, in a window that is not the timeline, and focus stays where it was (g:carto_flow_follow_edits, default on; :CartoFlowFollow flips it, :CartoFlowFollow on|off sets it). Relative frame paths resolve against g:carto_flow_roots, then the current directory.

These keys work in the timeline and in the carto-flow://frame detail:

KeyAction
]f / nnext frame (an open detail re-renders in place)
[f / pprevious frame
Glatest frame, and follow new ones
oopen the frame's code at its first changed line
<CR>(timeline) open the frame's detail and diff in a split
qclose the window
v / Vnext / previous layout (see below)
=re-fit the panel to the screen

Core's cursor drives this one: when anything moves the core timeline cursor (:carto-flow/next!, :carto-flow/previous!, :carto-flow/latest!, from Emacs, a tool call, or another vessel), the connected Vim's cursor moves to the same frame and an open detail re-renders. Moving with n/p inside Vim stays local to that Vim.

Layout

The timeline and the frame detail share one panel, docked on an edge of the screen and taking a fixed share of it; the code keeps the rest. A layout names both: left-1/3 is a full-height column a third of the screen wide, with the timeline stacked over the detail; bottom-1/2 is a full-width row half the screen tall, with the timeline beside the detail. Any of left, right, top and bottom with any fraction N/D works.

SettingDefault
g:carto_flow_layout'left-1/3' ('classic' when only g:carto_flow_position is set)
g:carto_flow_layouts['left-1/3', 'left-1/2', 'bottom-1/3', 'bottom-1/2'], the ones v/V cycle
g:carto_flow_timeline_share0.4, the timeline's part of the panel beside the detail

v and V in the panel, <leader>cv anywhere, and :CartoFlowLayout with no argument step through g:carto_flow_layouts; :CartoFlowLayout bottom-1/3 (tab-completes) docks on a named one. A shown panel re-docks at once and keeps focus where it was; a hidden one opens on the new layout next time. The panel re-fits on a terminal resize. classic is the old placement: the timeline at g:carto_flow_position, the detail split below it.

Global keys

The timeline keys above only work inside it, so the plugin also maps five actions globally. <leader> is your mapleader.

KeyAction
<leader>cf, <F9>show or hide the timeline (:CartoFlowToggle)
<leader>clopen it on the newest frame and follow from there
<leader>ce, <S-F9>follow live edits into the code, or stop
<leader>cddisconnect, or connect again
<leader>cvnext layout (:CartoFlowLayout)

Hiding the panel closes the timeline and the detail and keeps their buffers and frames, so the next toggle shows the same view rather than an empty one.

Each key is installed only when it is free and nothing you wrote already reaches that action, so a vimrc binding wins:

nmap <F5> <Plug>(carto-flow-toggle)   " the default <leader>cf and <F9> stand down

The five named mappings are <Plug>(carto-flow-toggle), <Plug>(carto-flow-latest), <Plug>(carto-flow-follow), <Plug>(carto-flow-connect) and <Plug>(carto-flow-layout). g:carto_flow_no_default_maps = 1 refuses the default set entirely and leaves them for you to bind.

Commands: :CartoFlow [port], :CartoFlowConnect [port], :CartoFlowDisconnect, :CartoFlowCode, :CartoFlowFollow [on|off], :CartoFlowToggle, :CartoFlowLayout [name], and :CartoFlowClear, which clears only this Vim's view. g:carto_flow_auto_open = 1 opens the timeline on the first live frame without moving focus.

Neovim is not supported, because it has no Vim JSON channels. A separate hive-carto-flow-nvim addon using msgpack-rpc is the intended route.

Hooks

  • :carto-flow.vim/port returns the listening port.
  • :carto-flow.vim/port-file returns the port file path Vim reads.
  • :carto-flow.vim/plugin-dir extracts the Vim plugin and returns its directory. Running it again is safe.
  • :carto-flow.vim/vessel returns the hive-vessel target of the connected Vim.

Development

clojure -Sdeps "$(cat local.deps.edn)" -M:test

local.deps.edn points hive-carto-flow, hive-vessel, hive-addon, hive-events and hive-dsl at sibling checkouts; clojure -M:test runs against the published jars instead, which is what CI does. The integration test drives a real headless /usr/bin/vim, and the provision test drives a real Vim in tmux started after injection; both are skipped when Vim or its features are missing.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close