1
Fork 0

Start working on a more modern latex snippet system

This commit is contained in:
prescientmoon 2024-03-14 07:48:19 +01:00
parent b0ff2979ed
commit a47be3adf9
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
5 changed files with 283 additions and 190 deletions

View file

@ -7,7 +7,7 @@ vim.opt.conceallevel = 0
local abbreviations = { local abbreviations = {
-- Other fancy symvols -- Other fancy symvols
{ "tmat", "^T" }, -- Tranpose of a matrix { "tmat", "^T" }, -- Transpose of a matrix
{ "cmat", "^*" }, -- Conjugate of a matrix { "cmat", "^*" }, -- Conjugate of a matrix
{ "sneg", "^C" }, -- Set complement { "sneg", "^C" }, -- Set complement
{ "ortco", "^\\bot" }, -- Orthogonal complement { "ortco", "^\\bot" }, -- Orthogonal complement
@ -79,7 +79,7 @@ local abbreviations = {
local abolishAbbreviations = { local abolishAbbreviations = {
-- {{{ Special chars -- {{{ Special chars
-- System for writing special characters which need to also be easly -- System for writing special characters which need to also be easily
-- accessible as {sub/super}scripts. -- accessible as {sub/super}scripts.
-- --
-- The reason epsilon and lambda are separated out from everything else in -- The reason epsilon and lambda are separated out from everything else in

View file

@ -9,6 +9,15 @@ function M.setup()
require("my.keymaps").setup() require("my.keymaps").setup()
require("my.lazy").setup() require("my.lazy").setup()
tempest.configureMany(nix.post) tempest.configureMany(nix.post)
vim.api.nvim_create_autocmd("FileType", {
pattern = "tex",
group = vim.api.nvim_create_augroup("luasnip-latex-snippets", {}),
once = true,
callback = function()
require("my.snippets.tex").setup()
end,
})
end end
return M return M

View file

@ -0,0 +1,263 @@
local M = {}
local ls = require("luasnip")
local extras = require("luasnip.extras")
local conds = require("luasnip.extras.expand_conditions")
local t = ls.text_node
local i = ls.insert_node
local d = ls.dynamic_node
local f = ls.function_node
local sn = ls.snippet_node
local fmt = require("luasnip.extras.fmt").fmt
local n = extras.nonempty
local autosnippets = {}
-- {{{ Helpers
local function unlines(...)
return table.concat({ ... }, "\n")
end
local function flatten(arr)
local result = {}
for _, subarray in ipairs(arr) do
if type(subarray) == "table" then
for _, value in ipairs(subarray) do
table.insert(result, value)
end
else
table.insert(result, subarray)
end
end
return result
end
local function trig(name)
return { name = name, trig = name }
end
-- }}}
-- {{{ Math mode detection
-- Taken from: https://github.com/iurimateus/luasnip-latex-snippets.nvim/blob/main/lua/luasnip-latex-snippets/util/ts_utils.lua
local MATH_NODES = {
displayed_equation = true,
inline_formula = true,
math_environment = true,
}
local TEXT_NODES = {
text_mode = true,
label_definition = true,
label_reference = true,
}
local function get_node_at_cursor()
local pos = vim.api.nvim_win_get_cursor(0)
-- Subtract one to account for 1-based row indexing in nvim_win_get_cursor
local row, col = pos[1] - 1, pos[2]
local parser = vim.treesitter.get_parser(0, "latex")
if not parser then
return
end
local root_tree = parser:parse({ row, col, row, col })[1]
local root = root_tree and root_tree:root()
if not root then
return
end
return root:named_descendant_for_range(row, col, row, col)
end
local function in_text(check_parent)
local node = get_node_at_cursor()
while node do
if node:type() == "text_mode" then
if check_parent then
-- For \text{}
local parent = node:parent()
if parent and MATH_NODES[parent:type()] then
return false
end
end
return true
elseif MATH_NODES[node:type()] then
return false
end
node = node:parent()
end
return true
end
local function in_mathzone()
local node = get_node_at_cursor()
while node do
if TEXT_NODES[node:type()] then
return false
elseif MATH_NODES[node:type()] then
return true
end
node = node:parent()
end
return false
end
local function not_math()
return in_text(true)
end
-- }}}
-- {{{ Start of line & non-math
local beginTextCondition = function(...)
return conds.line_begin(...) and not_math()
end
local parseBeginText = ls.extend_decorator.apply(ls.parser.parse_snippet, {
condition = beginTextCondition,
}) --[[@as function]]
local snipBeginText = ls.extend_decorator.apply(ls.snippet, {
condition = beginTextCondition,
}) --[[@as function]]
local function env(name)
return "\\begin{" .. name .. "}\n\t$0\n\\end{" .. name .. "}"
end
local function optional_square_bracket_arg(index, default)
return sn(index, {
n(1, "[", ""),
i(1, default),
n(1, "]", ""),
})
end
local function theorem_env(name, prefix)
return snipBeginText(trig(name), {
t("\\begin{" .. name .. "}"),
optional_square_bracket_arg(1),
n(2, "\\label{" .. prefix .. ":", ""),
i(2),
n(2, "}", ""),
t({ "", "\t" }),
i(0),
t({ "", "\\end{" .. name .. "}" }),
})
end
vim.list_extend(autosnippets, {
parseBeginText(
{ trig = "begin", name = "Begin / end environment" },
env("$1")
),
-- {{{ Chapters / sections
parseBeginText(trig("chapter"), "\\chapter{$1}\n$0"),
parseBeginText(trig("section"), "\\section{$1}\n$0"),
parseBeginText(trig("subsection"), "\\subsection{$1}\n$0"),
parseBeginText(trig("subsubsection"), "\\subsubsection{$1}\n$0"),
-- }}}
-- {{{ Lists
parseBeginText({ trig = "item", name = "List item" }, "\\item"),
parseBeginText({ trig = "olist", name = "Ordered list" }, env("enumerate")),
parseBeginText({ trig = "ulist", name = "Unordered list" }, env("itemize")),
-- }}}
-- {{{ Theorem envs
theorem_env("theorem", "thm"),
theorem_env("lemma", "lem"),
theorem_env("exercise", "exe"),
theorem_env("definition", "def"),
theorem_env("corollary", "cor"),
theorem_env("example", "exa"),
snipBeginText(trig("proof"), {
t("\\begin{proof}"),
optional_square_bracket_arg(1),
t({ "", "\t" }),
i(0),
t({ "", "\\end{proof}" }),
}),
-- }}}
-- {{{ Special structures
parseBeginText(
{ trig = "ciff", name = "If and only if cases" },
unlines(
"\\begin{enumerate}",
"\t\\item[$\\implies$]$1",
"\t\\item[$\\impliedby$]$2",
"\\end{enumerate}",
"$0"
)
),
-- }}}
})
-- }}}
-- {{{ Non-math
local parseText = ls.extend_decorator.apply(ls.parser.parse_snippet, {
condition = not_math,
}) --[[@as function]]
local snipText = ls.extend_decorator.apply(ls.snippet, {
condition = not_math,
}) --[[@as function]]
local function ref(name, prefix)
return {
parseText(
{ trig = "r" .. name, name = name .. " reference" },
"\\ref{" .. prefix .. ":$1}$0"
),
parseText(
{ trig = "pr" .. name, name = name .. " reference" },
"(\\ref{" .. prefix .. ":$1})$0"
),
}
end
vim.list_extend(
autosnippets,
flatten({
-- {{{ References
ref("theorem", "thm"),
ref("lemma", "lem"),
ref("exercise", "exe"),
ref("definition", "def"),
ref("corollary", "cor"),
ref("example", "exa"),
{
parseText({ trig = "ref", name = "reference" }, "\\ref{$1}$0"),
parseText({ trig = "pref", name = "reference" }, "(\\ref{$1})$0"),
},
-- }}}
{
-- {{{ Misc
parseText(trig("quote"), "``$1''$0"),
parseText(trig("forcecr"), "{\\ \\\\\\\\}"),
-- }}}
-- {{{ Let ...
snipText(
{ trig = "([Ll]et)", trigEngine = "pattern", name = "definition" },
{
f(function(_, snip)
return snip.captures[1]
end),
t(" "),
sn(1, fmt("${} ≔ {}$", { i(1), i(2) })),
}
),
-- }}}
-- {{{ Display / inline math
parseText({ trig = "dm", name = "display math" }, env("align*")),
parseText({ trig = "im", name = "inline math" }, "\\$$1\\$$0"),
-- }}}
},
})
)
-- }}}
function M.setup()
ls.add_snippets("tex", autosnippets, {
type = "autosnippets",
default_priority = 0,
})
end
return M

View file

@ -846,20 +846,26 @@ let
# {{{ luasnip # {{{ luasnip
# snippeting engine # snippeting engine
luasnip = luasnip =
let reload = /* lua */ ''require("luasnip.loaders.from_vscode").lazy_load()''; let reloadVscode = /* lua */ ''require("luasnip.loaders.from_vscode").lazy_load()'';
in in
{ {
package = "L3MON4D3/LuaSnip"; package = "L3MON4D3/LuaSnip";
version = "v2"; version = "v2";
cond = blacklist "vscode"; cond = blacklist "vscode";
config = thunk reload; config = thunk ''
require("luasnip").config.setup(${encode {
enable_autosnippets = true;
update_events = ["TextChanged" "TextChangedI"];
}})
${reloadVscode}
'';
# {{{ Keybinds # {{{ Keybinds
keys = [ keys = [
{ {
mapping = "<leader>rs"; mapping = "<leader>rs";
action = thunk reload; action = thunk reloadVscode;
desc = "[R]eload [s]nippets"; desc = "[R]eload [s]nippets";
} }
{ {

View file

@ -1,13 +1,4 @@
{ {
"Begin": {
"prefix": "begin",
"description": "Begin anything",
"body": [
"\\begin{$1}",
"\t$0",
"\\end{$1}"
]
},
"Set": { "Set": {
"prefix": "set", "prefix": "set",
"description": "Set I guess", "description": "Set I guess",
@ -38,69 +29,6 @@
"description": "Inner product of a vector with itself", "description": "Inner product of a vector with itself",
"body": "\\dprod{$1}$0" "body": "\\dprod{$1}$0"
}, },
"Lemma": {
"prefix": "lemma",
"description": "Create a lemma",
"body": [
"\\begin{lemma}[$1]\\label{lem:$1}",
"\t$0",
"\\end{lemma}"
]
},
"Example*": {
"prefix": "example*",
"description": "Create an example*",
"body": [
"\\begin{example*}",
"\t$0",
"\\end{example*}"
]
},
"Example": {
"prefix": "example",
"description": "Create an example",
"body": [
"\\begin{example}[$1]\\label{exp:$1}",
"\t$0",
"\\end{example}"
]
},
"Theorem": {
"prefix": "theorem",
"description": "Create a theorem",
"body": [
"\\begin{theorem}[$1]\\label{thm:$1}",
"\t$0",
"\\end{theorem}"
]
},
"Exercise": {
"prefix": "exercise",
"description": "Create a exercise",
"body": [
"\\begin{exercise}[$1]\\label{exe:$1}",
"\t$0",
"\\end{exercise}"
]
},
"Definition": {
"prefix": "definition",
"description": "Create a definition",
"body": [
"\\begin{definition}[$1]\\label{def:$1}",
"\t$0",
"\\end{definition}"
]
},
"Display math": {
"prefix": "dm",
"description": "Display math section",
"body": [
"\\[",
"$0",
"\\]"
]
},
"Subscript": { "Subscript": {
"prefix": "ss", "prefix": "ss",
"description": "Subscript", "description": "Subscript",
@ -126,80 +54,6 @@
"description": "The set of Z/nZ", "description": "The set of Z/nZ",
"body": "\\mathbb{Z}/$1\\mathbb{Z}$0" "body": "\\mathbb{Z}/$1\\mathbb{Z}$0"
}, },
"Section": {
"prefix": "section",
"description": "Add section",
"body": [
"\\section{$1}",
"$0"
]
},
"Subsection": {
"prefix": "subsection",
"description": "Add subsection",
"body": [
"\\subsection{$1}",
"$0"
]
},
"Subsubsection": {
"prefix": "subsubsection",
"description": "Add subsubsection",
"body": [
"\\subsubsection{$1}",
"$0"
]
},
"Chapter": {
"prefix": "chapter",
"description": "Add chapter",
"body": [
"\\chapter{$1}",
"$0"
]
},
"Proof": {
"prefix": "proof",
"description": "Create proof",
"body": [
"\\begin{proof}",
"\t$0",
"\\end{proof}"
]
},
"Itemize": {
"prefix": "item",
"body": [
"\\\\begin{itemize}",
"\t\\item $0",
"\\\\end{itemize}"
],
"description": "Itemize env"
},
"Enumerate": {
"prefix": "enum",
"body": [
"\\\\begin{enumerate}",
"\t\\item $0",
"\\\\end{enumerate}"
],
"description": "Enumerate env"
},
"Reference definition": {
"prefix": "rdef",
"description": "Reference a definition",
"body": "\\ref{def:$1}$0"
},
"Reference lemma": {
"prefix": "rlemma",
"description": "Reference a lemma",
"body": "\\ref{lem:$1}$0"
},
"Reference theorem": {
"prefix": "rtheorem",
"description": "Reference a theorem",
"body": "\\ref{thm:$1}$0"
},
"Sigma sum": { "Sigma sum": {
"prefix": "bsum", "prefix": "bsum",
"description": "Create a sum using sigma notation", "description": "Create a sum using sigma notation",
@ -260,34 +114,11 @@
"description": "Create a ln call", "description": "Create a ln call",
"body": "\\ln($1)$0" "body": "\\ln($1)$0"
}, },
"Aligned": {
"prefix": "aligned",
"description": "Create an aligned environment",
"body": [
"\\begin{aligned}",
"\t$0",
"\\end{aligned}"
]
},
"Let": { "Let": {
"prefix": "let", "prefix": "let",
"description": "Let something equal something else", "description": "Let something equal something else",
"body": "Let $$1 = $2$. $0" "body": "Let $$1 = $2$. $0"
}, },
"Force newline": {
"prefix": "cr",
"description": "Force newline in math mode",
"body": "{\\ \\\\\\\\}"
},
"Aligned display math": {
"prefix": "maligned",
"description": "Create an aligned display math environment",
"body": [
"\\begin{align*}",
"\t$0",
"\\end{align*}"
]
},
"System of equations": { "System of equations": {
"prefix": "eqsystem", "prefix": "eqsystem",
"description": "Create a system of equations", "description": "Create a system of equations",
@ -358,21 +189,5 @@
"prefix": "integral", "prefix": "integral",
"description": "Integral", "description": "Integral",
"body": "\\int $1 d${2:x}$0" "body": "\\int $1 d${2:x}$0"
},
"Iff cases": {
"prefix": "ciff",
"description": "Prove an equivalence in both directions",
"body": [
"\\begin{enumerate}",
"\t\\item[$\\implies$]$1",
"\t\\item[$\\impliedby$]$2",
"\\end{enumerate}",
"$0"
]
},
"quote": {
"prefix": "quote",
"description": "Quote a bunch of text",
"body": "``$1''$0"
} }
} }