Poke databases from your nvim like a professional idiot.
  • Lua 99.8%
  • Shell 0.2%
Find a file
2026-06-13 16:01:52 +02:00
dev feat(browser): tonic browse 2026-04-20 10:03:26 +02:00
doc feat(results): render query-file report comments 2026-06-13 16:01:52 +02:00
lua/tonic feat(results): render query-file report comments 2026-06-13 16:01:52 +02:00
plugin feat: Init 2026-04-19 21:11:53 +02:00
tests feat: Init 2026-04-19 21:11:53 +02:00
.gitignore feat(dev): dev setup 2026-04-19 21:19:23 +02:00
LICENSE feat: Init 2026-04-19 21:11:53 +02:00
README.md feat(results): render query-file report comments 2026-06-13 16:01:52 +02:00
VERSION feat(results): render query-file report comments 2026-06-13 16:01:52 +02:00

tonic.nvim

Neovim client for tonic serve --stdio.

tonic.nvim is a separate plugin repo that treats tonic as the stable execution engine and uses Neovim + nui.nvim for the editor UX.

Requirements

  • Neovim >= 0.12
  • MunifTanjim/nui.nvim
  • tonic on your $PATH (plugin defaults currently require tonic >= 0.0.3)

Installation

lazy.nvim

{
  url = "https://forgejo.fairlabs.dev/ferrumboll/tonic.nvim.git",
  name = "tonic.nvim",
  dependencies = {
    "MunifTanjim/nui.nvim",
  },
  config = function()
    require("tonic").setup()
  end,
}

Commands

  • :Tonic run Runs the current statement.
  • :'<,'>Tonic run Runs exact selected text + top file directives.
  • :Tonic run-buffer Runs the full buffer.
  • :Tonic browse Opens the catalog browser rooted at configured targets.
  • :Tonic browse-toggle Toggles the catalog browser for the current buffer.
  • :Tonic target Opens a nui.menu picker and stores the fallback target in vim.b.tonic_target.
  • :Tonic targets Shows the available tonic targets.
  • :Tonic status Shows the current client and buffer status.
  • :Tonic restart Restarts the background tonic serve --stdio process.
  • :checkhealth tonic Verifies the plugin, nui.nvim, the tonic CLI handshake, catalog browser methods, and optional native completion support.

Behavior

Current Statement

Tonic run in normal mode sends the full buffer to query.planArtifact, finds the statement covering the cursor, and then executes that exact statement index with query.executeArtifact.

Visual Selection

Tonic run in visual mode builds a synthetic artifact from:

  1. leading -- tonic-file: and -- tonic-bind: directives before the first SQL statement
  2. the exact selected text

For v0.1.0 this is intentionally strict:

  • partial statement selections may fail
  • per-statement -- tonic: overrides only apply if that directive line is inside the selection

Results UI

tonic.nvim uses:

  • nui.split for the persistent bottom results pane
  • nui.menu for target selection
  • nui.popup for status and detail views
  • native vim.notify(...) notifications for errors and warnings, which integrate with UIs like noice.nvim

The table view trims wide cells to keep the results buffer readable. In the results buffer, press K on any data cell to inspect the full untrimmed value in a popup.

When tonic returns ordered artifact events, the results pane renders tonic-print report text in source order around statement results. This supports query files such as:

-- tonic-print: # Daily sales report
-- tonic-print:
-- tonic-print-begin
-- ## Notes
--
-- - Generated for finance
-- tonic-print-end

select * from reports.daily_sales;

Report comments require tonic 0.8.0 or newer.

Catalog Browser

:Tonic browse opens a persistent tree browser rooted at all configured targets, then catalog, namespace, table, view, and column navigation. For MySQL targets, databases are shown as catalogs and expand directly to table/view groups because MySQL does not expose a separate namespace scope.

The browser is intentionally lazy in v0.1.8:

  • target, catalog, namespace, table, and view children load when expanded
  • moving the cursor only updates the selected node and does not make background requests
  • ? opens a help popup with browser-local key hints for the selected node
  • K opens a hover-style popup near the cursor with the selected node metadata, descriptions, loaded columns, DDL, or view definition
  • d fetches DDL when the backend supports it and shows it in the popup
  • v fetches view SQL for view nodes and shows it in the popup
  • F writes or replaces the top-file tonic-file directive using canonical context.catalog and context.namespace fields
  • B inserts a new top-file tonic-bind directive with an auto-generated handle like warehouse-1, warehouse-2, warehouse-3

Browser keymaps:

  • <CR> or l to expand the current node
  • h to collapse the current node or move to its parent
  • ? to open browser help
  • K to open details for the selected node
  • r to refresh the selected node
  • R to rebuild the browser from the current target list
  • d to load DDL in a popup when supported
  • v to load a view definition in a popup for view nodes
  • F to add or replace the file-scoped tonic-file directive
  • B to append a file-scoped tonic-bind directive
  • q to close the browser

Native Completion

tonic.nvim registers Neovim's native user completion for configured SQL-like buffers by setting a buffer-local completefunc. Trigger it with Neovim's built-in <C-x><C-u> completion flow or map that key sequence yourself.

Completion is optional against older tonic servers. :checkhealth tonic reports completion.items separately from required startup and catalog browser methods: missing completion support is a warning, not a plugin startup failure. During editing, unsupported servers, request failures, timeouts, or malformed completion responses quietly produce an empty completion list instead of popups or interrupted typing.

The plugin calls only the completion.items JSON-RPC method for completion. It does not call catalog browser inspect methods, does not request live refresh, and relies on tonic core's cache-only completion semantics. Configure inspect cache path and TTL in tonic config, and use browser refresh when you intentionally want live metadata refetch for browsing.

Configuration

require("tonic").setup({
  cmd = { "tonic", "serve", "--stdio" },
  minimum_tonic_version = "0.0.3",
  browser = {
    request_timeout = 30000,
    position = "left",
    size = { width = 36 },
    popup = {
      size = { width = 72, height = 20 },
    },
    help_popup = {
      size = { width = 44, height = 14 },
    },
  },
  completion = {
    enabled = true,
    filetypes = { "sql" },
    request_timeout = nil,
  },
  highlight = {
    enabled = true,
    filetypes = { "sql" },
  },
  request_timeout = 10000,
  startup_timeout = 5000,
  result = {
    position = "bottom",
    size = "35%",
    enter = false,
    focusable = true,
    max_cell_width = 40,
  },
})

request_timeout controls query RPC calls such as planning and execution. browser.request_timeout is used for browser metadata requests like catalogs, schemas, tables, views, DDL, and view definitions, so you can allow slower introspection without slowing normal query runs. completion.request_timeout overrides the global timeout for native completion requests; leave it as nil to use request_timeout.

completion.enabled controls buffer-local native completion registration, and completion.filetypes selects which normal buffers receive tonic's completefunc. The default enables SQL buffers only.

minimum_tonic_version lets the plugin reject older tonic serve binaries during the startup capability handshake before any commands run.

Browser inspect metadata is now cached persistently by tonic itself. Configure the cache path and TTL in tonic config, not in tonic.nvim, and use browser refresh (r) when you want to force a live refetch from the source database.

Directive Highlighting

tonic.nvim highlights directive comments in enabled filetypes without LSP or Tree-sitter.

By default it applies buffer-local extmark highlights for:

  • tonic, tonic-file, tonic-bind, tonic-print, tonic-print-begin, and tonic-print-end directive names
  • directive keys like target, handle, autocommit, and context.schema
  • = and : operators
  • directive values
  • invalid tokens or invalid fields for that directive kind

The default filetype list is { "sql" }, and you can override it with highlight.filetypes.

vim.keymap.set("n", "<leader>tr", "<cmd>Tonic run<CR>", { desc = "Tonic run statement" })
vim.keymap.set("x", "<leader>tr", ":Tonic run<CR>", { desc = "Tonic run selection" })
vim.keymap.set("n", "<leader>tb", "<cmd>Tonic run-buffer<CR>", { desc = "Tonic run buffer" })
vim.keymap.set("n", "<leader>tc", "<cmd>Tonic browse-toggle<CR>", { desc = "Tonic toggle catalog browser" })
vim.keymap.set("n", "<leader>tt", "<cmd>Tonic target<CR>", { desc = "Tonic pick target" })
vim.keymap.set("n", "<leader>tl", "<cmd>Tonic targets<CR>", { desc = "Tonic list targets" })
vim.keymap.set("n", "<leader>ts", "<cmd>Tonic status<CR>", { desc = "Tonic status" })
vim.keymap.set("n", "<leader>tx", "<cmd>Tonic restart<CR>", { desc = "Tonic restart" })

Note: the visual-mode mapping intentionally uses :Tonic run<CR> instead of <cmd>... so Neovim preserves the visual range and tonic.nvim receives :'<,'>Tonic run.

Development

Run the headless tests with:

nvim --headless -u tests/minimal_init.lua -c "lua require('tonic.tests.run').run()"

For interactive plugin development, this repo ships with an isolated lazy.nvim config that installs lazy.nvim and nui.nvim under repo-local XDG paths.

Launch it with:

./dev/run.sh

This uses:

  • XDG_CONFIG_HOME=$REPO/dev
  • XDG_DATA_HOME=$REPO/.data
  • XDG_STATE_HOME=$REPO/.state
  • XDG_CACHE_HOME=$REPO/.cache

The dev config lives in dev/nvim/ and loads:

  • MunifTanjim/nui.nvim
  • the local tonic.nvim plugin from this repo

The isolated dev config also ships with these default keymaps:

  • <leader>tr to run the current statement in normal mode, or the exact selection in visual mode
  • <leader>tb to run the full buffer
  • <leader>tc to open the catalog browser
  • <leader>tt to pick a fallback target
  • <leader>tl to list targets
  • <leader>ts to show status
  • <leader>tx to restart the background tonic serve process

0.1.14

  • Render tonic-print query-file report events in the results pane while preserving legacy statement rendering when no report text is present.
  • Highlight tonic-print, tonic-print-begin, and tonic-print-end directive comments in SQL buffers.

0.1.13

  • Add native completion health diagnostics that warn when optional completion.items support is unavailable without failing plugin startup.
  • Document <C-x><C-u> completion usage, completion configuration, timeout behavior, cache-only semantics, and quiet degradation for older or unavailable servers.

0.1.12

  • Show inspect descriptions in browser details when tonic provides them, including Snowflake object comments exposed as descriptions.

0.1.11

  • Enforce a minimum supported tonic version during the server.capabilities handshake so newer plugin features fail fast with a clear compatibility error.
  • Add semver compatibility tests and document the plugin-side minimum_tonic_version setting.

0.1.10

  • Make browser refresh (r) force live inspect refetches so it can bypass tonic's persistent metadata cache when needed.
  • Keep normal browser expansion on tonic's default cached inspect path while leaving cache TTL policy in tonic config.

0.1.9

  • Switch tonic.nvim errors from nui.popup windows to native vim.notify(...) notifications for better integration with UIs like noice.nvim.
  • Add browser.request_timeout so browser metadata inspection can use a separate timeout from query execution.

0.1.8

  • Replace the split preview pane in :Tonic browse with a tree-only browser plus popup-driven details on K.
  • Add browser-local key help on ?, with context-aware hints similar to Neo-tree.

0.1.7

  • Rework :Tonic browse into a target-first tree so browsing starts from the configured targets without a target picker popup.
  • Add F and B browser actions to write top-file tonic-file and tonic-bind directives, with canonical scope fields and auto-generated bind handles.

0.1.6

  • Add :Tonic browse, a persistent split-based catalog browser with a tree pane and preview pane built on nui.layout and nui.tree.
  • Add lazy inspection for catalogs, namespaces, tables, views, columns, DDL, and view definitions with browser-specific keymaps and health reporting.

0.1.5

  • Keep table cells trimmed in the results pane while adding K to inspect the full value of the cell under the cursor.
  • Preserve full row values in results metadata so popup inspection can show untrimmed strings and structured values.

0.1.4

  • Add built-in directive highlighting for tonic, tonic-file, and tonic-bind comments using Lua parsing and extmark highlights.
  • Add configuration for highlighted filetypes and parser tests for directive tokens and rendered extmarks.

0.1.3

  • Update the README installation snippet to use the Forgejo tonic.nvim repository URL.

0.1.2

  • Add default tonic.nvim keymaps to the isolated dev config.
  • Document recommended user keymaps in the README.

0.1.1

  • Add a repo-local lazy.nvim development config and launcher script for isolated nui.nvim and plugin testing.

0.1.0

  • Add a runner-first MVP around a persistent tonic serve --stdio JSON-RPC client.
  • Add current statement, visual selection, and full-buffer execution flows with nui.nvim status, target, result, and error UIs.