zeta $\zeta$
June 5, 2025 ยท View on GitHub
A typst language-server for zettelkasten-style note-taking with reference tracking and navigation.
Language Server Features
- Go to Definition navigates directly to referenced notes.
- Find References locates all notes that reference the current note (backlinks).
- Workspace Symbols show all notes by name and path. (Best used with Telescope)
- Document Diagnostics hint a links resolved path.
Installation
Download the latest release. Make the binary executable and place it in your path. Done!
Build from source (with nix)
on any host
Clone the repo and build the binary with nix.
git clone git@github.com:lentilus/zeta.git
cd zeta && nix build .#zeta
The binary is statically linked. Nix is only needed for the build.
in a nix flake
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
zeta = {
url = "github:lentilus/zeta";
inputs.nixpkgs.follows = "nixpkgs";
};
...
};
outputs = { nixpkgs, zeta, ... }: let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
zeta = zeta.packages.${system}.zeta;
in {
...
};
}
Configuration
Zeta is configured entirely through the initialization_options. Below is an example for neovim. The setup for other editors is analogous.
vim.lsp.config['zeta'] = {
cmd = { 'zeta' },
filetypes = { 'typst' },
root_markers = { 'test.typ' },
init_options = {} -- zeta has defaults
on_attach = function()
print("Zeta attached!")
end,
}
vim.lsp.enable('zeta')
The default init_options are
defaults = {
-- A treesitter query that is used to extract the necessary
-- information from notes. The @target capture is mandatory.
-- It is piped through the select_regex to extract a references target.
query = [[
(code (call item: (ident) @link (#eq? @link "link") (group (string) @target )))
(heading (text) @title)
(heading (label) @taxon)
]],
-- These are the names of the captures that are used to generate a notes title
-- The captured values are plugged into the title template in the order they appear.
title_substitutions = {"taxon", "title"},
title_template = "%s %s",
-- The regex used to select a substring from the tree-sitter @target capture.
-- If the regex yields multiple captures, the first is used.
select_regex = '^"(.*)"$',
-- The default extension to use if a target does not have one.
default_extension = ".typ",
-- The file extension's of files that zeta should look at.
-- This is especially important for zeta to be able to detect notes not opened in
-- the editor.
file_extensions = {".typ"},
}
Contribute
Contributions are very welcome!