使用 LuaSnip 片段在 Neovim 中创建 LaTeX 表

问题描述 投票:0回答:1

当我在 Neovim 中使用 LaTeX 写数学笔记时,LuaSnip 非常有帮助。但是,我无法实现构建不同表(表、表格、数组等)的片段。我可以使用哪些片段?

我知道这个插件(https://github.com/evesdropper/luasnip-latex-snippets.nvim),但我想使用我自己的片段。

latex code-snippets neovim neovim-plugin
1个回答
0
投票

LuaSnip wiki 中有一个详细记录的 example,它展示了如何制作一个可以创建 LaTeX 表并动态调整行数的代码片段(滚动到页面底部以查看其实际效果)。

这为随后用于数组、矩阵等提供了一个很好的起点。然而,这是我不建议开始使用的更复杂的片段之一,除非您已经尝试过 LuaSnip 的各种节点,特别是功能节点动态节点,并且对它们的工作方式感到满意。

我将在此处重现该示例,以避免将来出现潜在的死链接问题。以下内容全部归功于 LuaSnip (L3MON4D3) 的作者。

编写一个运行其他函数的函数,其输出将在动态节点函数中使用;并更新动态节点。

local ls = require("luasnip")
local util = require("luasnip.util.util")
local node_util = require("luasnip.nodes.util")

local function find_dynamic_node(node)
    -- the dynamicNode-key is set on snippets generated by a dynamicNode only (its'
    -- actual use is to refer to the dynamicNode that generated the snippet).
    while not node.dynamicNode do
        node = node.parent
    end
    return node.dynamicNode
end

local external_update_id = 0
-- func_indx to update the dynamicNode with different functions.
function dynamic_node_external_update(func_indx)
    -- most of this function is about restoring the cursor to the correct
    -- position+mode, the important part are the few lines from
    -- `dynamic_node.snip:store()`.


    -- find current node and the innermost dynamicNode it is inside.
    local current_node = ls.session.current_nodes[vim.api.nvim_get_current_buf()]
    local dynamic_node = find_dynamic_node(current_node)

    -- to identify current node in new snippet, if it is available.
    external_update_id = external_update_id + 1
    current_node.external_update_id = external_update_id

    -- store which mode we're in to restore later.
    local insert_pre_call = vim.fn.mode() == "i"
    -- is byte-indexed! Doesn't matter here, but important to be aware of.
    local cursor_pos_pre_relative = util.pos_sub(
        util.get_cursor_0ind(),
        current_node.mark:pos_begin_raw()
    )

    -- leave current generated snippet.
    node_util.leave_nodes_between(dynamic_node.snip, current_node)

    -- call update-function.
    local func = dynamic_node.user_args[func_indx]
    if func then
        -- the same snippet passed to the dynamicNode-function. Any output from func
        -- should be stored in it under some unused key.
        func(dynamic_node.parent.snippet)
    end

    -- last_args is used to store the last args that were used to generate the
    -- snippet. If this function is called, these will most probably not have
    -- changed, so they are set to nil, which will force an update.
    dynamic_node.last_args = nil
    dynamic_node:update()

    -- everything below here isn't strictly necessary, but it's pretty nice to have.


    -- try to find the node we marked earlier.
    local target_node = dynamic_node:find_node(function(test_node)
        return test_node.external_update_id == external_update_id
    end)

    if target_node then
        -- the node that the cursor was in when changeChoice was called exists
        -- in the active choice! Enter it and all nodes between it and this choiceNode,
        -- then set the cursor.
        node_util.enter_nodes_between(dynamic_node, target_node)

        if insert_pre_call then
            util.set_cursor_0ind(
                util.pos_add(
                    target_node.mark:pos_begin_raw(),
                    cursor_pos_pre_relative
                )
            )
        else
            node_util.select_node(target_node)
        end
        -- set the new current node correctly.
        ls.session.current_nodes[vim.api.nvim_get_current_buf()] = target_node
    else
        -- the marked node wasn't found, just jump into the new snippet noremally.
        ls.session.current_nodes[vim.api.nvim_get_current_buf()] = dynamic_node.snip:jump_into(1)
    end
end

将功能绑定到某个按键。

vim.api.nvim_set_keymap('i', "<C-t>", '<cmd>lua _G.dynamic_node_external_update(1)<Cr>', {noremap = true})
vim.api.nvim_set_keymap('s', "<C-t>", '<cmd>lua _G.dynamic_node_external_update(1)<Cr>', {noremap = true})

vim.api.nvim_set_keymap('i', "<C-g>", '<cmd>lua _G.dynamic_node_external_update(2)<Cr>', {noremap = true})
vim.api.nvim_set_keymap('s', "<C-g>", '<cmd>lua _G.dynamic_node_external_update(2)<Cr>', {noremap = true})

绑定更多数字可能会很有用(3-???),但对于本例来说两个就足够了。现在是时候使用新功能了。

local function column_count_from_string(descr)
    -- this won't work for all cases, but it's simple to improve
    -- (feel free to do so! :D )
    return #(descr:gsub("[^clm]", ""))
end

-- function for the dynamicNode.
local tab = function(args, snip)
    local cols = column_count_from_string(args[1][1])
    -- snip.rows will not be set by default, so handle that case.
    -- it's also the value set by the functions called from dynamic_node_external_update().
    if not snip.rows then
        snip.rows = 1
    end
    local nodes = {}
    -- keep track of which insert-index we're at.
    local ins_indx = 1
    for j = 1, snip.rows do
        -- use restoreNode to not lose content when updating.
        table.insert(nodes, r(ins_indx, tostring(j).."x1", i(1)))
        ins_indx = ins_indx+1
        for k = 2, cols do
            table.insert(nodes, t" & ")
            table.insert(nodes, r(ins_indx, tostring(j).."x"..tostring(k), i(1)))
            ins_indx = ins_indx+1
        end
        table.insert(nodes, t{"\\\\", ""})
    end
    -- fix last node.
    nodes[#nodes] = t""
    return sn(nil, nodes)
end


s("tab", fmt([[
\begin{{tabular}}{{{}}}
{}
\end{{tabular}}
]], {i(1, "c"), d(2, tab, {1}, {
    user_args = {
        -- Pass the functions used to manually update the dynamicNode as user args.
        -- The n-th of these functions will be called by dynamic_node_external_update(n).
        -- These functions are pretty simple, there's probably some cool stuff one could do
        -- with `ui.input`
        function(snip) snip.rows = snip.rows + 1 end,
        -- don't drop below one.
        function(snip) snip.rows = math.max(snip.rows - 1, 1) end
    }
} )}))
© www.soinside.com 2019 - 2024. All rights reserved.