Neovim plugin for efficient (mathematical) note taking in Typst
See changes in CHANGELOG.md
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:
:<char> → $<char>$ in markup (e.g. :X → $X$ , :5 → $5$ );<latin> → <greek> in math and $<greek>$ in markup (e.g. ;a → alpha/$alpha$ )i-n): <letter><index> → <letter>_<index> in math and $<letter>$ <index> → $<letter>_<index>$ in markup (e.g A314 → A_314 , $alpha$ n → $alpha_n$ , $F$ n, → $F_n$, , $F$ n. → $F_n$.)$<letter>$ ' → $<letter>'$ and in combination with index and punctuation like above (e.g. $phi$ ' → $phi'$ , $phi$ 5' → $phi'_5$ , $f$ '5. → $f'_5$., f'5, → f'_5, )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:
kk and multiline math with dmHIG → #highlight[<cursor>], IMP → $==>$ )thm → empty theorem, exm → empty example)fla and flAMath snippets:
<letter> <z/o>t<optional last index> → <letter>_<0/1>, <letter>_<1/2>, ... (e.g. a ot → a_1, a_2, ... , a zt4 → a_0, a_1, a_2, a_3, a_4 , alpha otk → alpha_1, alpha_2, ..., alpha_k , oti → 1, 2, ..., i )<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)])fo<value> → f(<value>) (e.g. fox → f(x) , ao5 → a(5) )<size>ma and <size>ma. (e.g. 23ma → 2x3 matrix and 32ma. → 3x2 matrix with dots)Note that you can customize (enable, disable and modify) every snippet.
:TypstarInsertExcalidraw/:TypstarInsertRnote to
create a new drawing using the configured template,
insert a figure displaying it and open it in Obsidian/Rnote.: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
}
// ANKI: MY::DECK to your document to set a deck used for all flashcards after this comment (You can use multiple decks per file).anki containing a deck name to define a default deck on a directory base.anki.typ to define a preamble on a directory base. You can find the default preamble here.:TypstarAnkiScan to scan the current nvim working directory and compile all flashcards in its context, unchanged files will be ignored: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):TypstarAnkiForceCurrent to force compilation of all flashcards in the file currently edited:TypstarAnkiReimport to also add flashcards that have already been assigned an id but are not currently
present in Anki:TypstarAnkiForceReimport and :TypstarAnkiForceCurrentReimport to combine features accordinglytypstar-anki --help to show the available optionsInstall the plugin in Neovim and run the plugin setup. To run a demo installation, see Demo. To use Nix for installation, see Nix.
require('typstar').setup({ -- depending on your neovim plugin system
-- your typstar config goes here
})
{
"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,
cut_selection_keys = "<Tab>",
})
end,
},
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
branch = "main",
lazy = false,
config = function()
require('nvim-treesitter').install { "typst" }
end
},
0.14. For older versions check out the legacy typst-0.13 branch.enable_autosnippets = true and set a visual mode selection key (e.g. cut_selection_keys = '<Tab>') in the configuration:TSInstall typst<C-g>. Otherwise set add_undo_breakpoints = false in the configAuto-export SVG (in plugin settings at Embedding Excalidraw into your Notes and Exporting > Export Settings > Auto-export Settings)xdg-open command working or set a different command at uriOpenCommand in the config~/typstar or setting the typstarRoot config accordingly; feel free to open an issuernote-cli is available in your PATH or set a different command at exportCommand in the configxdg-open command working with Rnote files or set a different command at uriOpenCommand in the confighttp://localhost is added to webCorsOriginList in the Add-on config (should be added by default)uv tool install typstar, using pipx should also work)typstar-anki command is available in your PATH or modify the typstarAnkiCmd option in the configA basic demo setup using either Nix or Lazy is provided. The keybindings are defined here.
For Nix: Run nix run github:arne314/typstar#nvim -- test.typ (~200MB download).
For Lazy: Clone the repo with git clone https://github.com/arne314/typstar.git and run
just lazy if you have just installed or run ./res/lazy/lazy.sh test.typ if you don't.
You will need to have nvim and tree-sitter available in your PATH.
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 packages
(pkgs.vimUtils.buildVimPlugin {
name = "typstar";
src = inputs.typstar;
buildInputs = with pkgs.vimPlugins; [
luasnip
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
snippets.enable = falsesnippets.modules = { 'letters' }snippets.exclude = { 'dx', 'ddx' }visual module
snippets.visual_disable = { 'br' }abs → abs(1+1)) via e.g. snippets.visual_disable_normal = { 'abs' }xabs → abs(x)) via e.g. snippets.visual_disable_postfix = { 'abs' }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),
}
Feel free to open an issue or a PR.
For development with Nix, a shell is provided, which you can enter via nix develop.
Running nvim from within the shell will launch a minimal installation of the plugin, sourced at startup, so no additional nix build is needed.
Tests can be executed using just test from within the shell or via nix flake check.
The code can be linted using just lint.
For development without Nix, run just lazy for a local lazy installation which sources the plugin on startup.
To run tests in lazy, run just test-lazy.
Run just --list for more details.
Lua
71.2%
Python
23.1%
Nix
3.9%
Just
1.2%
Neovim plugin for efficient (mathematical) note taking in Typst
See changes in CHANGELOG.md
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:
:<char> → $<char>$ in markup (e.g. :X → $X$ , :5 → $5$ );<latin> → <greek> in math and $<greek>$ in markup (e.g. ;a → alpha/$alpha$ )i-n): <letter><index> → <letter>_<index> in math and $<letter>$ <index> → $<letter>_<index>$ in markup (e.g A314 → A_314 , $alpha$ n → $alpha_n$ , $F$ n, → $F_n$, , $F$ n. → $F_n$.)$<letter>$ ' → $<letter>'$ and in combination with index and punctuation like above (e.g. $phi$ ' → $phi'$ , $phi$ 5' → $phi'_5$ , $f$ '5. → $f'_5$., f'5, → f'_5, )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:
kk and multiline math with dmHIG → #highlight[<cursor>], IMP → $==>$ )thm → empty theorem, exm → empty example)fla and flAMath snippets:
<letter> <z/o>t<optional last index> → <letter>_<0/1>, <letter>_<1/2>, ... (e.g. a ot → a_1, a_2, ... , a zt4 → a_0, a_1, a_2, a_3, a_4 , alpha otk → alpha_1, alpha_2, ..., alpha_k , oti → 1, 2, ..., i )<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)])fo<value> → f(<value>) (e.g. fox → f(x) , ao5 → a(5) )<size>ma and <size>ma. (e.g. 23ma → 2x3 matrix and 32ma. → 3x2 matrix with dots)Note that you can customize (enable, disable and modify) every snippet.
:TypstarInsertExcalidraw/:TypstarInsertRnote to
create a new drawing using the configured template,
insert a figure displaying it and open it in Obsidian/Rnote.: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
}
// ANKI: MY::DECK to your document to set a deck used for all flashcards after this comment (You can use multiple decks per file).anki containing a deck name to define a default deck on a directory base.anki.typ to define a preamble on a directory base. You can find the default preamble here.:TypstarAnkiScan to scan the current nvim working directory and compile all flashcards in its context, unchanged files will be ignored: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):TypstarAnkiForceCurrent to force compilation of all flashcards in the file currently edited:TypstarAnkiReimport to also add flashcards that have already been assigned an id but are not currently
present in Anki:TypstarAnkiForceReimport and :TypstarAnkiForceCurrentReimport to combine features accordinglytypstar-anki --help to show the available optionsInstall the plugin in Neovim and run the plugin setup. To run a demo installation, see Demo. To use Nix for installation, see Nix.
require('typstar').setup({ -- depending on your neovim plugin system
-- your typstar config goes here
})
{
"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,
cut_selection_keys = "<Tab>",
})
end,
},
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
branch = "main",
lazy = false,
config = function()
require('nvim-treesitter').install { "typst" }
end
},
0.14. For older versions check out the legacy typst-0.13 branch.enable_autosnippets = true and set a visual mode selection key (e.g. cut_selection_keys = '<Tab>') in the configuration:TSInstall typst<C-g>. Otherwise set add_undo_breakpoints = false in the configAuto-export SVG (in plugin settings at Embedding Excalidraw into your Notes and Exporting > Export Settings > Auto-export Settings)xdg-open command working or set a different command at uriOpenCommand in the config~/typstar or setting the typstarRoot config accordingly; feel free to open an issuernote-cli is available in your PATH or set a different command at exportCommand in the configxdg-open command working with Rnote files or set a different command at uriOpenCommand in the confighttp://localhost is added to webCorsOriginList in the Add-on config (should be added by default)uv tool install typstar, using pipx should also work)typstar-anki command is available in your PATH or modify the typstarAnkiCmd option in the configA basic demo setup using either Nix or Lazy is provided. The keybindings are defined here.
For Nix: Run nix run github:arne314/typstar#nvim -- test.typ (~200MB download).
For Lazy: Clone the repo with git clone https://github.com/arne314/typstar.git and run
just lazy if you have just installed or run ./res/lazy/lazy.sh test.typ if you don't.
You will need to have nvim and tree-sitter available in your PATH.
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 packages
(pkgs.vimUtils.buildVimPlugin {
name = "typstar";
src = inputs.typstar;
buildInputs = with pkgs.vimPlugins; [
luasnip
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
snippets.enable = falsesnippets.modules = { 'letters' }snippets.exclude = { 'dx', 'ddx' }visual module
snippets.visual_disable = { 'br' }abs → abs(1+1)) via e.g. snippets.visual_disable_normal = { 'abs' }xabs → abs(x)) via e.g. snippets.visual_disable_postfix = { 'abs' }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),
}
Feel free to open an issue or a PR.
For development with Nix, a shell is provided, which you can enter via nix develop.
Running nvim from within the shell will launch a minimal installation of the plugin, sourced at startup, so no additional nix build is needed.
Tests can be executed using just test from within the shell or via nix flake check.
The code can be linted using just lint.
For development without Nix, run just lazy for a local lazy installation which sources the plugin on startup.
To run tests in lazy, run just test-lazy.
Run just --list for more details.
Lua
71.2%
Python
23.1%
Nix
3.9%
Just
1.2%