ibid is a high-performance, editor-agnostic Language Server Protocol (LSP) daemon written in Rust. It integrates your live Zotero library directly into your text editor alongside whichever language server already understands your document, providing lightning-fast autocompletion, rich hover cards, citekey diagnostics with quickfix imports, and native Zotero Citation-As-You-Write (CAYW) picker dialogs.
-
True Coexistence, Zero UI Clutter:
ibidis designed as an auxiliary language server. It coexists cleanly with primary language servers liketinymist(Typst),texlab(LaTeX), andmarksman(Markdown). When an incumbent server manages local.bibcompletion,ibidseamlessly defers local items and provides your live remote Zotero library candidates without duplicate completion entries. -
Sub-Millisecond Speed: In-memory bibliography indexing and token-based search provide autocompletion responses in
$< 50\text{ ms}$ under live editing, and instant$0\text{ ms}$ fast-path responses when offline. - Resilient & Idempotent: A fault-tolerant BibTeX parser with boundary chunk recovery ensures that malformed syntax in draft documents does not take down your completion engine.
-
Clean Import Pipeline: When you accept a Zotero completion candidate or trigger a quickfix code action,
ibidfetches the full BibLaTeX entry via Better BibTeX's export translator and atomically appends it to your workspace.bibfile with collision avoidance. -
Universal Client Support: First-class support for Zed (
zed-ibid), Neovim (ibid.nvim), Helix, and standard LSP clients.
ibid includes dedicated, zero-dependency syntactic parsers for all major academic writing formats:
| Language | Recognized Citation Patterns | Supported Incumbents |
|---|---|---|
| Markdown / Pandoc / Quarto / RMarkdown | [@key], @key, [-@key; @key2, p. 42], key: [@value] |
marksman |
| Typst | @key, <key>, #cite(<key>), #bibliography("refs.bib") |
tinymist |
| LaTeX | \cite{key}, \citep{key}, \parencite[42]{key}, \addbibresource{refs.bib} |
texlab |
| Plain Text | @key |
None (Owned) |
- Combines local workspace
.bibcitations with live search results from Better BibTeX (BBT). - Implements
ANDtoken filtering with smart prefix matching across citekeys, titles, authors, and publication years. - If Zotero is closed or unreachable, autocompletion falls back instantaneously to local files with zero latency penalties.
- Hovering over
@citekeydisplays a beautifully formatted Markdown card with Title, Authors, Year, Journal/Container, DOI, and Citation Count. - Remote Zotero items indicate their library origin.
- Unresolved citekeys are flagged with descriptive diagnostics:
"Unknown citation '@citekey' (available in Zotero library)"(if present in Zotero)."Unknown citation '@citekey'"(if unknown everywhere).
- Provides an LSP Code Action (
quickfix) to immediately import the entry into your workspace bibliography.
- Trigger
:ZoteroCite/zotero.caywto open Zoteroβs native red citation picker dialog. - Selected citations are formatted per your document's language and inserted at your cursor.
- Citekeys present in the returned citation are automatically imported into your workspace
.bibfile in the background. - Built-in single-flight concurrency lock prevents duplicate picker windows from wedging Zotero.
- Pressing
gdon a citation key in Markdown or Plain Text jumps directly to the corresponding entry definition inside your workspace.bibfile.
βοΈ Incumbent Coexistence Model (ADR-05)
To prevent UI duplication (e.g. duplicate completion entries or competing diagnostics), ibid implements a 3-tier deferral resolution ladder for each document language:
Layer 1 (Highest): Explicit user array in settings (e.g. "deferToIncumbent": ["typst"])
β (if absent or "auto")
Layer 2: Adaptive client detection (Neovim LspAttach / Zed which probe)
β (if detection yields no attached incumbents)
Layer 3 (Fallback): Static default fallback ["typst", "latex"]
| Feature | Owned Languages (Markdown, Text) | Deferred Languages (tinymist / texlab present) |
|---|---|---|
Local .bib Completion |
β
Served by ibid |
βοΈ Deferred to incumbent |
| Remote Zotero Completion | β
Served by ibid |
β
Served by ibid |
| Hover on Local Key | β
Served by ibid |
βοΈ Deferred to incumbent |
| Hover on Remote Zotero Key | β
Served by ibid |
β
Served by ibid |
| Unknown Citekey Diagnostic | β
Emitted by ibid |
βοΈ Deferred to incumbent |
Code Action (quickfix import) |
β
Provided by ibid |
βοΈ Deferred (use :ZoteroImport or accept completion) |
Go-to-Definition (gd) |
β
Jump to .bib |
βοΈ Handled by incumbent |
| Write Operations (CAYW / Import) | β Unconditional | β Unconditional (always active) |
Download the matching binary for your platform from GitHub Releases:
| Platform | Target Asset |
|---|---|
| macOS (Apple Silicon) | ibid-aarch64-apple-darwin |
| macOS (Intel) | ibid-x86_64-apple-darwin |
| Linux (x86_64) | ibid-x86_64-unknown-linux-gnu |
| Linux (ARM64) | ibid-aarch64-unknown-linux-gnu |
| Windows (x86_64) | ibid-x86_64-pc-windows-msvc.exe |
Place the binary on your system $PATH as ibid (or ibid.exe).
git clone https://github.com/r11r/ibid.git
cd ibid
cargo install --path crates/ibid --lockedInstall the "Ibid β Zotero citations" extension from the Zed extensions gallery (or use zed-ibid).
Add to your Zed settings.json:
{
"lsp": {
"ibid": {
"settings": {
"bbt": {
"host": "127.0.0.1",
"port": 23119
},
"bibFile": "references.bib",
"deferToIncumbent": "auto",
"unknownCitekeySeverity": "warning"
}
}
}
}Optional CAYW keybinding in keymap.json:
[
{
"context": "Editor && (mode == full)",
"bindings": {
"ctrl-alt-z": [
"editor::ExecuteCommand",
{
"command": "zotero.cayw"
}
]
}
}
]Use ibid.nvim with your favorite plugin manager (e.g. lazy.nvim):
{
"r11r/ibid.nvim",
ft = { "markdown", "quarto", "rmd", "typst", "tex", "plaintex", "text" },
opts = {
settings = {
bibFile = "references.bib",
deferToIncumbent = "auto",
unknownCitekeySeverity = "warning",
},
},
keys = {
{ "<C-A-z>", "<Plug>(ibid-cite)", mode = { "n", "i" }, desc = "Zotero CAYW Cite" },
},
}Run :checkhealth ibid to verify server status, Better BibTeX port connectivity, and completion engine capability.
Add ibid as an auxiliary language server in ~/.config/helix/languages.toml:
[language-server.ibid]
command = "ibid"
[[language]]
name = "markdown"
language-servers = ["marksman", "ibid"]
[[language]]
name = "typst"
language-servers = ["tinymist", "ibid"]
[[language]]
name = "latex"
language-servers = ["texlab", "ibid"]To import a citekey under your cursor in Helix, use Space + a (Code Actions) on the unknown citation diagnostic.
All settings can be forwarded under the ibid initialization options or via workspace/didChangeConfiguration:
| Key | Type | Default | Description |
|---|---|---|---|
bibFile |
string |
null |
Primary target .bib filename (e.g. "references.bib", "main.bib"). If null, ibid scans the workspace. |
bbt.host |
string |
"127.0.0.1" |
Better BibTeX JSON-RPC host. |
bbt.port |
number |
23119 |
Better BibTeX JSON-RPC port. |
bbt.searchTimeoutMs |
number |
150 |
Maximum wait time for live Zotero autocompletion requests. |
bbt.exportTimeoutMs |
number |
2000 |
Timeout for BibLaTeX item exports during import. |
bbt.probeTimeoutMs |
number |
150 |
Timeout for health checks. |
bbt.translator |
string |
"Better BibLaTeX" |
Export translator ("Better BibLaTeX" or "Better BibTeX"). |
caywFormat |
string |
"auto" |
CAYW output format ("auto", "typst", "latex", "pandoc", "citekey"). |
deferToIncumbent |
string | string[] |
"auto" |
Incumbent deferral policy ("auto", [], or explicit language list like ["typst"]). |
unknownCitekeySeverity |
string |
"warning" |
Severity for unknown citekeys ("warning", "error", "information", "hint", "off"). |
The project is structured as a pure-library / async-server workspace:
ibid/
βββ crates/
β βββ ibid-core/ # Pure, zero-IO domain crate (WebAssembly/no-std ready)
β β βββ src/bib/ # Hayagriva-backed resilient parser, search index, atomic writer
β β βββ src/context/ # Syntactic citation context detection (Typst, LaTeX, Markdown, Text)
β β βββ src/policy/ # Incumbent deferral logic (ADR-05)
β βββ ibid/ # Async LSP binary crate (tower-lsp, tokio, reqwest)
β βββ src/bbt/ # Better BibTeX JSON-RPC client, health FSM, version gate
β βββ src/backend/ # LSP request handlers, document position mapping, CAYW runner
β βββ src/main.rs # Stdio server entry point
βββ projects/
β βββ zed-ibid/ # Zed WASM extension (zed_extension_api = "0.7.0")
β βββ ibid.nvim/ # Neovim Lua plugin (0.10+ / 0.11+)
βββ docs/ # Architecture Decision Records (ADRs) & PRDs
ibid-corePurity Gate:ibid-corehas zero dependencies on async runtimes (tokio), networking (reqwest), or LSP transport (tower-lsp). All business logic, parsing, and data models remain purely synchronous and unit-testable.- Fast-Path Offline Bail: In steady-state offline mode, the health state machine guarantees that autocompletion never performs synchronous I/O or blocks the editor thread.
Run all unit and integration test suites:
# Format check
cargo fmt --all -- --check
# Strict lint check
cargo clippy --workspace --all-targets -- -D warnings
# Run all 40 workspace tests (includes stdio wiremock integration tests)
cargo test --workspace
# Check core crate purity gate
cargo tree -p ibid-core -e normal | grep -E '(tokio|reqwest|tower-lsp)' && exit 1 || echo "Purity gate OK"Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.