A Neovim plugin that provides edit completions and cursor predictions.
[!NOTE]
Help improve completions by contributing anonymous usage data to our open dataset. Set
contribute_data = truein your config to opt in. No code content or file paths are collected — see the schema.
<img src="https://github.com/cursortab/cursortab.nvim/raw/v0.8.0/assets/demo.gif" width="600">
Recommended starting points:
inline provider, or Sweep 1.5B/0.5B
with the sweep providerPick a provider below, then use the matching setup() call in your plugin
config. See Providers for all available options.
bash
export MERCURY_AI_TOKEN="your-api-key-here"
Run llama.cpp:
llama-server -hf bartowski/zed-industries_zeta-2-GGUF:Q8_0 --port 8000
Run llama.cpp:
llama-server -hf unsloth/Qwen3.5-0.8B-GGUF:Q8_0 --port 8000
# llama-server -hf sweepai/sweep-next-edit-0.5b --port 8000
# llama-server -hf sweepai/sweep-next-edit-1.5b --port 8000
{
"cursortab/cursortab.nvim",
-- version = "*", -- Use latest tagged version for more stability
lazy = false, -- The server is already lazy loaded
build = "cd server && go build",
config = function()
require("cursortab").setup({
provider = {
-- Mercury API (hosted)
type = "mercuryapi",
api_key_env = "MERCURY_AI_TOKEN",
-- Zeta-2 (best local)
-- type = "zeta-2",
-- url = "http://localhost:8000",
-- Qwen3.5-0.8B (fastest local, defaults to "inline")
-- url = "http://localhost:8000",
-- sweep-next-edit-0.5B/1.5B (fastest local)
-- type = "sweep",
-- url = "http://localhost:8000",
},
})
end,
}
use {
"cursortab/cursortab.nvim",
-- tag = "*", -- Use latest tagged version for more stability
run = "cd server && go build",
config = function()
require("cursortab").setup({
provider = {
type = "mercuryapi",
api_key_env = "MERCURY_AI_TOKEN",
},
})
end
}
Full config
require("cursortab").setup({
enabled = true,
log_level = "info", -- "trace", "debug", "info", "warn", "error"
state_dir = vim.fn.stdpath("state") .. "/cursortab", -- Directory for runtime files (log, socket, pid)
contribute_data = false, -- Opt-in: send anonymous metrics to train a better gating model
keymaps = {
accept = "<Tab>", -- Keymap to accept completion, or false to disable
partial_accept = "<S-Tab>", -- Keymap to partially accept, or false to disable
trigger = false, -- Keymap to manually trigger completion, or false to disable
},
ui = {
completions = {
addition_style = "dimmed", -- "dimmed" or "highlight"
fg_opacity = 0.6, -- opacity for completion overlays (0=invisible, 1=fully visible)
},
jump = {
symbol = "", -- Symbol shown for jump points
text = " TAB ", -- Text displayed after jump symbol
show_distance = true, -- Show line distance for off-screen jumps
},
},
behavior = {
idle_completion_delay = 50, -- Delay in ms after idle to trigger completion (-1 to disable)
text_change_debounce = 50, -- Debounce in ms after text change to trigger completion (-1 to disable)
max_visible_lines = 12, -- Max visible lines per completion (0 to disable)
disabled_in = {}, -- Tree-sitter scopes to suppress completions (e.g., { "comment", "string" })
enabled_modes = { "insert", "normal" }, -- Modes where completions are active
cursor_prediction = {
enabled = true, -- Show jump indicators after completions
auto_advance = true, -- When no changes, show cursor jump to last line
proximity_threshold = 2, -- Min lines apart to show cursor jump (0 to disable)
},
ignore_paths = { -- Glob patterns for files to skip completions
"*.min.js",
"*.min.css",
"*.map",
"*-lock.json",
"*.lock",
"*.sum",
"*.csv",
"*.tsv",
"*.parquet",
"*.zip",
"*.tar",
"*.gz",
"*.pem",
"*.key",
".env",
".env.*",
"*.log",
},
ignore_filetypes = { "", "terminal" }, -- Filetypes to skip completions
ignore_gitignored = true, -- Skip files matched by .gitignore
},
provider = {
type = "inline", -- Provider: "inline", "fim", "sweep", "zeta", "zeta-2", "copilot", or "mercuryapi"
url = "http://localhost:8000", -- URL of the provider server
api_key_env = "", -- Env var name for API key (e.g., "OPENAI_API_KEY")
model = "", -- Model name
temperature = 0.0, -- Sampling temperature
context_size = 0, -- Max input context in tokens (0 = use max_tokens; inline/fim default: 1024)
max_tokens = 512, -- Max tokens to generate (inline default: 64, fim default: 128)
top_k = 50, -- Top-k sampling
completion_timeout = 5000, -- Timeout in ms for completion requests
max_diff_history_tokens = 512, -- Max tokens for diff history (0 = no limit)
completion_path = "/v1/completions", -- API endpoint path
-- fim_tokens is optional. Omit (the default) to use OpenAI prompt+suffix
-- format (e.g. DeepSeek). Set it to opt into tokenized FIM:
-- fim_tokens = {
-- prefix = "<|fim_prefix|>",
-- suffix = "<|fim_suffix|>",
-- middle = "<|fim_middle|>",
-- repo_name = "<|repo_name|>", -- optional; auto-detected for Qwen
-- file_sep = "<|file_sep|>", -- optional; auto-detected for Qwen
-- },
privacy_mode = true, -- Don't send telemetry to provider
},
blink = {
enabled = false, -- Enable blink source
ghost_text = true, -- Show native ghost text alongside blink menu
},
debug = {
immediate_shutdown = false, -- Shutdown daemon immediately when no clients
},
})
You can also run :help cursortab-config to see the configuration.
The plugin defines the following highlight groups with default = true, so you
can override them in your colorscheme or config:
| Group | Default | Purpose |
|---|---|---|
CursorTabDeletion |
bg = "#4f2f2f" |
Background for deleted text |
CursorTabAddition |
bg = "#394f2f" |
Background for added text |
CursorTabModification |
bg = "#282e38" |
Background for modified text |
CursorTabCompletion |
fg = "#80899c" |
Foreground for completion text |
CursorTabJumpSymbol |
fg = "#373b45" |
Jump indicator symbol |
CursorTabJumpText |
bg = "#373b45", fg = "#bac1d1" |
Jump indicator text |
To customize, set the highlight before or after calling setup():
vim.api.nvim_set_hl(0, "CursorTabAddition", { bg = "#1a3a1a" })
The plugin supports seven AI provider backends: Inline, FIM, Sweep, Zeta-2, Zeta (legacy), Copilot, and Mercury API.
| Provider | Hosted | Multi-line | Multi-edit | Cursor Prediction | Streaming | Model |
|---|---|---|---|---|---|---|
inline |
Any base model | |||||
fim |
✓ | ✓ | Any FIM-capable | |||
sweep |
✓ | ✓ | ✓ | ✓ | Sweep Next-Edit family | |
zeta-2 |
✓ | ✓ | ✓ | ✓ | zeta-2 (SeedCoder-8B) |
|
zeta |
✓ | ✓ | ✓ | ✓ | zeta (Qwen2.5-Coder) |
|
copilot |
✓ | ✓ | ✓ | ✓ | GitHub Copilot | |
mercuryapi |
✓ | ✓ | ✓ | ✓ | mercury-edit-2 |
Context Per Provider:
| Context | inline | fim | sweep | zeta-2 | zeta | copilot | mercuryapi |
|---|---|---|---|---|---|---|---|
| Buffer content | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Edit history | ✓° | ✓ | ✓ | ✓ | ✓ | ||
| Previous file state | ✓ | ||||||
| LSP diagnostics | ✓° | ✓ | ✓ | ✓ | ✓ | ||
| Treesitter context | ✓° | ✓ | ✓ | ✓ | ✓ | ||
| Git diff context | ✓° | ✓ | ✓ | ✓ | ✓ | ||
| Recent files | ✓° | ✓ | ✓ | ✓ | ✓ | ||
| User actions | ✓ |
° FIM cross-file context requires repo-level tokens (repo_name, file_sep).
Auto-detected for Qwen models; set manually for other models that support them.
Measured on 50 scenarios (25 quality + 25 suppress) using the eval harness. Sorted by Score (higher = better):
deltaChrF × gateScore / 100 where
gateScore = 2 × showRate × quietRate / (showRate + quietRate) (harmonic mean
/ F1). Combines edit quality with gating behavior into a single metric.| Target | Type | Score | deltaChrF | Show rate | Quiet rate | p50 (ms) | p90 (ms) |
|---|---|---|---|---|---|---|---|
| zeta-2 | zeta-2 | 0.61 | 65.4 | 92% | 96% | 551 | 833 |
| zeta | zeta | 0.56 | 62.4 | 88% | 92% | 413 | 662 |
| mercuryapi | mercuryapi | 0.49 | 61.8 | 92% | 69% | 332 | 393 |
| qwen3.6-27B | fim | 0.23 | 32.0 | 60% | 92% | 214 | 455 |
| sweep-next-edit-7B | sweep | 0.23 | 46.0 | 64% | 40% | 270 | 515 |
| qwen3.5-0.8B | fim | 0.21 | 37.3 | 80% | 44% | 137 | 226 |
| sweep-next-edit-1.5B | sweep | 0.21 | 43.7 | 68% | 36% | 157 | 258 |
| qwen3.5-2B | fim | 0.20 | 35.1 | 80% | 44% | 185 | 429 |
| qwen3.5-4B | fim | 0.18 | 28.7 | 64% | 64% | 357 | 730 |
| copilot | copilot | 0.13 | 22.3 | 40% | 100% | 351 | 915 |
| sweep-next-edit-0.5B | sweep | 0.10 | 23.0 | 52% | 40% | 126 | 207 |
| qwen3.6-35B-A3B | fim | 0.10 | 19.2 | 40% | 80% | 113 | 411 |
Details
Single-line completion using any OpenAI-compatible /v1/completions endpoint.
require("cursortab").setup({
provider = {
type = "inline",
url = "http://localhost:8000",
},
})
llama-server -hf unsloth/Qwen3.5-0.8B-GGUF:Q8_0 --port 8000
Details
Fill-in-the-Middle multi-line completion. Compatible with Qwen, DeepSeek-Co
$ claude mcp add cursortab.nvim \
-- python -m otcore.mcp_server <graph>