Neovim plugin for efficient (mathematical) note taking in Typst
- Powerful autosnippets using LuaSnip and Tree-sitter (inspired by fastex.nvim)
- Easy insertion of drawings using Obsidian Excalidraw or Rnote
- Export of Anki flashcards [No Neovim required]
Use :TypstarToggleSnippets
to toggle all snippets at any time.
To efficiently navigate insert nodes and avoid overlapping ones,
use :TypstarSmartJump
and :TypstarSmartJumpBack
.
Available snippets can mostly be intuitively derived from here, they include:
Universal snippets:
- Alphanumeric characters:
:<char>
→$<char>$
in markup (e.g.:X
→$X$
,:5
→$5$
) - Greek letters:
;<latin>
→<greek>
in math and$<greek>$
in markup (e.g.;a
→alpha
/$alpha$
) - Common indices (numbers and letters
i-n
):<letter><index>
→<letter>_<index>
in math and$<letter>$<index>
→$<letter>_<index>$
in markup (e.gA314
→A_314
,$alpha$n
→$alpha_n$
)
You can find a complete map of latin to greek letters including reasons for the less intuitive ones here. Note that some greek letters have multiple latin ones mapped to them.
Markup snippets:
- Begin inline math with
ll
and multiline math withdm
- Markup shorthands (e.g.
HIG
→#highlight[<cursor>]
,IMP
→$==>$
) - ctheorems shorthands (e.g.
tem
→ empty theorem,exa
→ empty example) - Flashcards:
fla
andflA
- All above snippets support visual mode via the selection key
Math snippets:
- Many shorthands for mathematical expressions
- Series of numbered letters:
<letter> ot<optional last index>
→<letter>_1, <letter>_2, ...
(e.g.a ot
→a_1, a_2, ...
,a ot4
→a_1, a_2, a_3, a_4
,alpha otk
→alpha_1, alpha_2, ..., alpha_k
) - Wrapping of any mathematical expression (see operations, works nested, multiline and in visual mode via the selection key):
<expression><operation>
→<operation>(<expression>)
(e.g.(a^2+b^2)rt
→sqrt(a^2+b^2)
,lambdatd
→tilde(lambda)
,(1+1)sQ
→[1+1]
,(1+1)sq
→[(1+1)]
) - Simple functions:
fo<value>
→f(<value>)
(e.g.fox
→f(x)
,ao5
→a(5)
) - Matrices:
<size>ma
and<size>lma
(e.g.23ma
→ 2x3 matrix)
Note that you can customize (enable, disable and modify) every snippet.
- Use
:TypstarInsertExcalidraw
/:TypstarInsertRnote
to create a new drawing using the configured template, insert a figure displaying it and open it in Obsidian/Rnote. - To open an inserted drawing in Obsidian/Rnote,
simply run
:TypstarOpenDrawing
(or:TypstarOpenExcalidraw
/:TypstarOpenRnote
if you are using the same file extension for both) while your cursor is on a line referencing the drawing.
Use the flA
snippet to create a new flashcard
#flashcard(0, "My first flashcard")[
Typst is awesome $a^2+b^2=c^2$
]
or the fla
snippet to add a more complex front
#flashcard(0)[I love Typst $pi$][
This is the back of my second flashcard
]
To render the flashcard in your document as well add some code like this
#let flashcard(id, front, back) = {
strong(front)
[\ ]
back
}
- Add a comment like
// ANKI: MY::DECK
to your document to set a deck used for all flashcards after this comment (You can use multiple decks per file) - Add a file named
.anki
containing a deck name to define a default deck on a directory base - Add a file named
.anki.typ
to define a preamble on a directory base. You can find the default preamble here. - Tip: Despite the use of SVGs you can still search your flashcards in Anki as the typst source is added into an invisible html paragraph
- Use
:TypstarAnkiScan
to scan the current nvim working directory and compile all flashcards in its context, unchanged files will be ignored - Use
:TypstarAnkiForce
to force compilation of all flashcards in the current working directory even if the files haven't changed since the last scan (e.g. on preamble change) - Use
:TypstarAnkiForceCurrent
to force compilation of all flashcards in the file currently edited - Use
:TypstarAnkiReimport
to also add flashcards that have already been asigned an id but are not currently present in Anki - Use
:TypstarAnkiForceReimport
and:TypstarAnkiForceCurrentReimport
to combine features accordingly
- Run
typstar-anki --help
to show the available options
Install the plugin in Neovim (see Nix instructions) and run the plugin setup.
require('typstar').setup({ -- depending on your neovim plugin system
-- your typstar config goes here
})
Example lazy.nvim config
{
"arne314/typstar",
dependencies = {
"L3MON4D3/LuaSnip",
},
ft = { "typst" },
keys = {
{
"<M-t>",
"<Cmd>TypstarToggleSnippets<CR>",
mode = { "n", "i" },
},
{
"<M-j>",
"<Cmd>TypstarSmartJump<CR>",
mode = { "s", "i" },
},
{
"<M-k>",
"<Cmd>TypstarSmartJumpBack<CR>",
mode = { "s", "i" },
},
},
config = function()
local typstar = require("typstar")
typstar.setup({
-- your typstar configuration
add_undo_breakpoints = true,
})
end,
},
{
"L3MON4D3/LuaSnip",
version = "v2.*",
build = "make install_jsregexp",
config = function()
local luasnip = require("luasnip")
luasnip.config.setup({
enable_autosnippets = true,
store_selection_keys = "<Tab>",
})
end,
},
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
lazy = false,
config = function()
local configs = require("nvim-treesitter.configs")
configs.setup({
ensure_installed = { "typst" },
})
end,
},
- Install LuaSnip, set
enable_autosnippets = true
and set a visual mode selection key (e.g.store_selection_keys = '<Tab>'
) in the configuration - Install jsregexp as described here (You will see a warning on startup if jsregexp isn't installed properly)
- Install nvim-treesitter and run
:TSInstall typst
- Make sure you haven't remapped
<C-g>
. Otherwise setadd_undo_breakpoints = false
in the config - Optional: Setup ctheorems with names like here
- Install Obsidian and create a vault in your typst note taking directory
- Install the obsidian-excalidraw-plugin and enable
Auto-export SVG
(in plugin settings atEmbedding Excalidraw into your Notes and Exporting > Export Settings > Auto-export Settings
) - Have the
xdg-open
command working or set a different command aturiOpenCommand
in the config - If you encounter issues with the file creation of drawings, try cloning the repo into
~/typstar
or setting thetypstarRoot
config accordingly; feel free to open an issue
- Install Rnote; I recommend not using flatpak as that might cause issues with file permissions.
- Make sure
rnote-cli
is available in yourPATH
or set a different command atexportCommand
in the config - Have the
xdg-open
command working with Rnote files or set a different command aturiOpenCommand
in the config - See comment 4 above at Excalidraw
- Typst version
0.12.0
or higher is required - Install Anki
- Install Anki-Connect and make sure
http://localhost
is added towebCorsOriginList
in the Add-on config (should be added by default) - Install the typstar python package (I recommend using pipx via
pipx install git+https://github.com/arne314/typstar
, you will need to have python build tools and clang installed) [Note: this may take a while] - Make sure the
typstar-anki
command is available in yourPATH
or modify thetypstarAnkiCmd
option in the config
You can add typstar to your nix-flake
like so
# `flake.nix`
inputs = {
# ... other inputs
typstar = {
url = "github:arne314/typstar";
flake = false;
};
}
Now you can use typstar
in any package-set
with pkgs; [
# ... other packges
(pkgs.vimUtils.buildVimPlugin {
name = "typstar";
src = inputs.typstar;
buildInputs = [
vimPlugins.luasnip
vimPlugins.nvim-treesitter-parsers.typst
];
})
]
Configuration options can be intuitively derived from the table here.
The templatePath
option expects a table that maps file patterns to template locations.
To for example have a specific template for lectures, you could configure it like this
templatePath = {
{ 'lectures/.*%.excalidraw%.md$', '~/Templates/lecture_excalidraw.excalidraw.md' }, -- path contains "lectures"
{ '%.excalidraw%.md$', '~/Templates/default_excalidraw.excalidraw.md' }, -- fallback
},
The config allows you to
- disable all snippets via
snippets.enable = false
- only include specific modules from the snippets folder via e.g.
snippets.modules = { 'letters' }
- exclude specific triggers via e.g.
snippets.exclude = { 'dx', 'ddx' }
- disable different behaviors of snippets from the
visual
module- visual selection via e.g.
snippets.visual_disable = { 'br' }
- normal snippets (
abs
→abs(1+1)
) via e.g.snippets.visual_disable_normal = { 'abs' }
- postfix snippets (
xabs
→abs(x)
) via e.g.snippets.visual_disable_postfix = { 'abs' }
- visual selection via e.g.
For further customization you can make use of the provided wrappers from within your LuaSnip config.
Let's say you prefer the short =>
arrow over the long ==>
one and would like to change the ip
trigger to imp
.
Your typstar
config could look like
require('typstar').setup({
snippets = {
exclude = { 'ip' },
},
})
while your LuaSnip typst.lua
could look like this (<
and >
require escaping as <>
introduces a new node)
local tp = require('typstar.autosnippets')
local snip = tp.snip
local math = tp.in_math
local markup = tp.in_markup
return {
-- add a new snippet (the old one is excluded via the config)
snip('imp', '=>> ', {}, math),
-- override existing triggers by setting a high priority
snip('ib', '<<= ', {}, math, 2000),
snip('iff', '<<=>> ', {}, math, 2000),
-- setup markup snippets accordingly
snip('IMP', '$=>>$ ', {}, markup, 2000),
snip('IFF', '$<<=>>$ ', {}, markup, 2000),
}