1

What I intend to do...

Initially, I wanted to write a series of environments that help me typeset verbatim environments. For example, I would like

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

to be equivalent to:

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

Essentially, each key corresponds to a specific combination of parameters for minted (or any other verbatim) environment. However, due to special implementations of verbatim environments in LaTeX, I realize that there is no easy way of doing this. By far, I only know two possible approaches:

  1. Use tcblisting+minted with a self-defined style and use \tcbset to change it programmatically.
  2. Save the entire verbatim environment as file and read with \input.

(I skimmed through tcblisting's source code, maybe these two approaches are fundamentally the same?)

Even though I think I have found a way of achieving my goals, I still want to ask the following questions because I am unable to explain many phenomena that happened throughout my own experiments. Please pardon my weak understandings on TeX and LuaTeX mechanisms.

My specific questions

  1. From my understanding, by saving the verbatim environment into files, we are able to decouple our code from regular TeX parsing routine. In this case, the alternative outlet is the file system. In LuaTeX, another scripting engine (i.e. Lua) is introduced apart from TeX engine. I would like to know if it is possible to use LuaTeX to do this directly, without the need of file system I/O? Using tex.print does not work work as expected (see question 2). Essentially, is it possible for LuaTeX to \input from Lua strings, instead of files?

  2. Why does the first one work but the second doesn't?

% 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. Why does tcblisting work with minted back-end?
% using minted via tcblisting
\directlua{tex.print([[\unexpanded{\begin{tcblisting}{listing engine=minted, minted language=python}]]..'one\rtwo'..[[\end{tcblisting}}]])}
  1. Why does \directlua work but luacode* doesn't?
\directlua{
tex.print([[\unexpanded{\begin{verbatim}abc\end{verbatim}}]])
}
\begin{luacode*}
tex.print([[\unexpanded{\begin{verbatim}abc\end{verbatim}}]])
\end{luacode*}

Possible preamble

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

\tcbuselibrary{listings, minted}
1

After reading the LuaTeX reference, I realize that it is possible to directly \input from Lua strings. The key is to override find_read_file and open_read_file callbacks, which allows us to write our own back-end for \input commands. For details, please see the code below.

\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} 

The output document looks like this: output document

I am not sure how much performance gain this solution has compared to file system I/O.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.