关于 LaTeX(和 LuaTeX)中的逐字记录

关于 LaTeX(和 LuaTeX)中的逐字记录

我打算做什么......

最初,我想编写一系列环境来帮助我排版逐字环境。例如,我想

\begin{myverb}{stylea}
\end{myverb}

相当于:

\begin{minted}{fontsize=\small, bgcolor=white}
\end{minted}

本质上,每个键都对应于minted(或任何其他逐字)环境的特定参数组合。但是,由于 LaTeX 中逐字环境的特殊实现,我意识到没有简单的方法可以做到这一点。到目前为止,我只知道两种可能的方法:

  1. 使用带有自定义样式的tcblisting+并使用编程方式进行更改。minted\tcbset
  2. 将整个 verbatim 环境保存为文件并用 读取\input

(我浏览了 的tcblisting源代码,也许这两种方法本质上是相同的?)

尽管我认为我已经找到了实现目标的方法,但我仍然想问以下问题,因为我无法解释在自己的实验中发生的许多现象。请原谅我对 TeX 和 LuaTeX 机制的理解不够。

我的具体问题

  1. 据我所知,通过将逐字环境保存到文件中,我们能够将代码与常规 TeX 解析例程分离。在这种情况下,替代出口是文件系统。在 LuaTeX 中,除了 TeX 引擎之外,还引入了另一个脚本引擎(即 Lua)。我想知道是否可以使用 LuaTeX 直接执行此操作,而无需文件系统 I/O?使用tex.print不起作用(参见问题 2)。本质上,LuaTeX 是否可以\input从 Lua 字符串而不是文件中提取?

  2. 为什么第一个有效而第二个无效?

% https://tex.stackexchange.com/questions/410481/verbatim-with-direct-lua/410482#410482
\directlua{tex.print([[\unexpanded{\begin{verbatim}]]..'one\rtwo'..[[\end{verbatim}}]])}
% using minted package
\directlua{tex.print([[\unexpanded{\begin{minted}{python}]]..'one\rtwo'..[[\end{minted}}]])}
  1. 为什么要tcblistingminted后端合作?
% using minted via tcblisting
\directlua{tex.print([[\unexpanded{\begin{tcblisting}{listing engine=minted, minted language=python}]]..'one\rtwo'..[[\end{tcblisting}}]])}
  1. 为什么\directlua起作用了但是luacode*不起作用呢?
\directlua{
tex.print([[\unexpanded{\begin{verbatim}abc\end{verbatim}}]])
}
\begin{luacode*}
tex.print([[\unexpanded{\begin{verbatim}abc\end{verbatim}}]])
\end{luacode*}

可能的序言

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{verbatim}
\usepackage{luacode}
\usepackage{minted}
\usepackage{tcolorbox}

\tcbuselibrary{listings, minted}

答案1

在阅读了 LuaTeX 参考资料后,我意识到可以直接\input从 Lua 字符串中获取。关键是覆盖find_read_fileopen_read_file回调,这使我们能够为命令编写自己的后端\input。有关详细信息,请参阅下面的代码。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{verbatim}
\usepackage{luacode, luatexbase}
\usepackage{tcolorbox}
\usepackage{minted}
\usepackage{datetime2}
\usepackage{expl3}
\usepackage{minted}

\tcbuselibrary{listings, minted}

\begin{document}

\begin{luacode*}
TeXFileBuffer = {content={}, finished=false}

function TeXFileBuffer:new()
    o = {}
    setmetatable(o, self)
    self.__index = self
    return o
end

function TeXFileBuffer:clear()
    while #self.content ~= 0 do rawset(self.content, #self.content, nil) end
end

function TeXFileBuffer:content_to_string()
    return table.concat(self.content, "")
end

function TeXFileBuffer:use()
    tex.write(self:content_to_string())
end

function TeXFileBuffer:append(data)
    table.insert(self.content, data)
end

function TeXFileBuffer:append_carriage_return(data)
    self:append("\r")
end

function _tex_buffer_remove_callback(name, description)
    for k, v in pairs(luatexbase.callback_descriptions(name)) do
        if v == description then
            texio.write("\nsafely removing callback " .. name .. " : " .. description)
            luatexbase.remove_from_callback(name, description)
        end
    end
end

function tex_buffer_remove_callback()
    _tex_buffer_remove_callback("find_read_file", "tex_file_buffer_find")
    _tex_buffer_remove_callback("open_read_file", "tex_file_buffer_read")
end

function tex_file_buffer_reader(env)
    local ret = nil
    if not env["finished"] then
        ret = env["content"]
        env["finished"] = true
        -- remove callback immediately
        tex_buffer_remove_callback()
    end
    return ret
end

function tex_file_buffer_find(id_number, asked_name)
    -- arguments and return value doesn't matter
    texio.write("\nTeXFileBuffer tries to find ".. asked_name .. " id=" .. id_number)
    return asked_name
end

function TeXFileBuffer:register_callback()
    tex_file_buffer_read = function (filename)
        local env = {}
        texio.write("\nTeXFileBuffer opens ".. filename)
        env["finished"] = false
        env["content"] = self:content_to_string()
        env["reader"] = tex_file_buffer_reader
        return env
    end

    -- register callback
    luatexbase.add_to_callback("find_read_file", tex_file_buffer_find, "tex_file_buffer_find")
    luatexbase.add_to_callback("open_read_file", tex_file_buffer_read, "tex_file_buffer_read")
end

-- create a TeXFilebuffer instance
tex_file_buffer = TeXFileBuffer:new()
\end{luacode*}

\makeatletter
\newcommand{\TFBInputAsFile}{
\directlua{tex_file_buffer:register_callback()}
% read some random file, which automatically removes the callback

% \input will do an file existance check before actually reading it.
% therefore, if using LaTeX's input, `random-file` will be opned twice
% here, I am using TeX's \@@input primitive instead
\@@input randomfile
}
\makeatother

\newcommand{\TFBAppend}[1]{
\directlua{tex_file_buffer:append("\luaescapestring{\unexpanded{#1}}")}
}

\newcommand{\TFBAppendCR}{
\directlua{tex_file_buffer:append_carriage_return()}
}

\newcommand{\TFBClear}{
\directlua{tex_file_buffer:clear()}
}

\newcommand{\TFBUse}{
\directlua{tex_file_buffer:use()}
}

\ExplSyntaxOn

\str_clear:N \l_tmpa_str
\exp_args:Nx \TFBAppend {\c_backslash_str begin{minted}{python}} \TFBAppendCR
\exp_args:Nx \TFBAppend {print("abc")} \TFBAppendCR
\exp_args:Nx \TFBAppend {\c_backslash_str end{minted}}

\ExplSyntaxOff

\TFBUse
\TFBInputAsFile

\par\DTMNow

\end{document} 

输出文档如下所示: 输出文件

我不确定与文件系统 I/O 相比,该解决方案的性能提升了多少。

相关内容