我打算做什么......
最初,我想编写一系列环境来帮助我排版逐字环境。例如,我想
\begin{myverb}{stylea}
\end{myverb}
相当于:
\begin{minted}{fontsize=\small, bgcolor=white}
\end{minted}
本质上,每个键都对应于minted
(或任何其他逐字)环境的特定参数组合。但是,由于 LaTeX 中逐字环境的特殊实现,我意识到没有简单的方法可以做到这一点。到目前为止,我只知道两种可能的方法:
- 使用带有自定义样式的
tcblisting
+并使用编程方式进行更改。minted
\tcbset
- 将整个 verbatim 环境保存为文件并用 读取
\input
。
(我浏览了 的tcblisting
源代码,也许这两种方法本质上是相同的?)
尽管我认为我已经找到了实现目标的方法,但我仍然想问以下问题,因为我无法解释在自己的实验中发生的许多现象。请原谅我对 TeX 和 LuaTeX 机制的理解不够。
我的具体问题
据我所知,通过将逐字环境保存到文件中,我们能够将代码与常规 TeX 解析例程分离。在这种情况下,替代出口是文件系统。在 LuaTeX 中,除了 TeX 引擎之外,还引入了另一个脚本引擎(即 Lua)。我想知道是否可以使用 LuaTeX 直接执行此操作,而无需文件系统 I/O?使用
tex.print
不起作用(参见问题 2)。本质上,LuaTeX 是否可以\input
从 Lua 字符串而不是文件中提取?为什么第一个有效而第二个无效?
% 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}}]])}
- 为什么要
tcblisting
与minted
后端合作?
% using minted via tcblisting
\directlua{tex.print([[\unexpanded{\begin{tcblisting}{listing engine=minted, minted language=python}]]..'one\rtwo'..[[\end{tcblisting}}]])}
- 为什么
\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_file
和open_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 相比,该解决方案的性能提升了多少。