tex2lyx: import minted listings

This commit updates tex2lyx in order to also import minted listings.
For the floating version of a listing, minted uses the listing
environment, a concept that is not shared with the listings package,
towards which our listings inset is geared.
For this reason, a kludge is necessary when importing minted listings
not previously exported by LyX itself.
If the floating listing contains only a caption and a label (other
than the listing itself), everything is fine and the import is (or
aims to be) perfect. But, as in all other floating ebvironments,
one can also stick there other elements, which don't have a place
in the listings inset. So, in order to avoid a data loss, tex2lyx
sticks everything into the caption. In this way, things may be
rearranged in the GUI, if necessary. There is no other way, apart
from a complete redesign of the listings inset, of course. However,
I think that this is an acceptable compromise.
This commit is contained in:
Enrico Forestieri 2017-06-17 02:23:00 +02:00
parent 195e55d3f4
commit 7a9bb85184
20 changed files with 245 additions and 38 deletions

View File

@ -183,9 +183,10 @@ const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
"amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
"float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
"makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
"rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
"tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
"makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
"rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
"tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
"xunicode", 0};
// codes used to remove packages that are loaded automatically by LyX.
// Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
@ -554,6 +555,7 @@ Preamble::Preamble() : one_language(true), explicit_babel(false),
h_use_hyperref = "false";
h_use_microtype = "false";
h_use_refstyle = false;
h_use_minted = false;
h_use_packages["amsmath"] = "1";
h_use_packages["amssymb"] = "0";
h_use_packages["cancel"] = "0";
@ -990,6 +992,8 @@ void Preamble::handle_package(Parser &p, string const & name,
else if (is_known(name, known_lyx_packages) && options.empty()) {
if (name == "splitidx")
h_use_indices = "true";
if (name == "minted")
h_use_minted = "true";
if (name == "refstyle")
h_use_refstyle = true;
else if (name == "prettyref")
@ -1261,7 +1265,8 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled
<< "\\paperorientation " << h_paperorientation << '\n'
<< "\\suppress_date " << h_suppress_date << '\n'
<< "\\justification " << h_justification << '\n'
<< "\\use_refstyle " << h_use_refstyle << '\n';
<< "\\use_refstyle " << h_use_refstyle << '\n'
<< "\\use_minted " << h_use_minted << '\n';
if (!h_fontcolor.empty())
os << "\\fontcolor " << h_fontcolor << '\n';
if (!h_notefontcolor.empty())

View File

@ -48,6 +48,8 @@ public:
std::string fontCJK() const { return h_font_cjk; }
///
void fontCJK(std::string const & f) { h_font_cjk_set = true; h_font_cjk = f; }
///
bool minted() const { return h_use_minted; }
/// The document language
std::string docLanguage() const { return h_language; }
/// The language of text which is not explicitly marked
@ -216,6 +218,7 @@ private:
std::string h_use_default_options;
std::string h_use_hyperref;
bool h_use_refstyle;
bool h_use_minted;
/*!
* Add package \p name with options \p options to used_packages.

View File

@ -151,19 +151,6 @@ Format LaTeX feature LyX feature
btprint "bibbysection"
534 Chapterbib support
\usepackage{chapterbib} \multibib child
544 Minted support
Non-floating: InsetListings
\begin{minted}[opts]{language}
...
\end{minted}
Floating:
\begin{listing}[placement]
\begin{minted}[opts]{language}
...
\end{minted}
\end{listing}
Inline (where '?' is any char):
\mintinline[opts]{language}?...?

View File

@ -63,6 +63,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -63,6 +63,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -61,6 +61,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -61,6 +61,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -62,6 +62,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -61,6 +61,7 @@ algorithm2e
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -87,6 +87,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\notefontcolor #0000ff
\backgroundcolor #ff5500
\boxbgcolor #ffff00

View File

@ -97,6 +97,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -73,6 +73,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -59,6 +59,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -59,6 +59,7 @@ theorems-ams
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -59,6 +59,7 @@ theorems-ams
\suppress_date false
\justification true
\use_refstyle 1
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -56,6 +56,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -92,6 +92,7 @@ logicalmkup
\suppress_date true
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -63,6 +63,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -52,6 +52,7 @@
\suppress_date false
\justification true
\use_refstyle 0
\use_minted 0
\index Index
\shortcut idx
\color #008000

View File

@ -321,6 +321,15 @@ char const * const known_tones[] = {"15", "51", "45", "12", "454", 0};
// string to store the float type to be able to determine the type of subfloats
string float_type = "";
// string to store the float status of minted listings
string minted_float = "";
// whether a caption has been parsed for a floating minted listing
bool minted_float_has_caption = false;
// The caption for non-floating minted listings
string minted_nonfloat_caption = "";
/// splits "x=z, y=b" into a map and an ordered keyword vector
void split_map(string const & s, map<string, string> & res, vector<string> & keys)
@ -1312,12 +1321,22 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
}
void parse_listings(Parser & p, ostream & os, Context & parent_context, bool in_line)
void parse_listings(Parser & p, ostream & os, Context & parent_context,
bool in_line, bool use_minted)
{
parent_context.check_layout(os);
begin_inset(os, "listings\n");
if (p.hasOpt()) {
string arg = p.verbatimOption();
string arg = p.hasOpt() ? p.verbatimOption() : string();
if (use_minted) {
string const language = p.getArg('{', '}');
p.skip_spaces(true);
arg += string(arg.empty() ? "" : ",") + "language=" + language;
if (!minted_float.empty()) {
arg += string(arg.empty() ? "" : ",") + minted_float;
minted_nonfloat_caption.clear();
}
}
if (!arg.empty()) {
os << "lstparams " << '"' << arg << '"' << '\n';
if (arg.find("\\color") != string::npos)
preamble.registerAutomaticallyLoadedPackage("color");
@ -1329,6 +1348,19 @@ void parse_listings(Parser & p, ostream & os, Context & parent_context, bool in_
os << "status collapsed\n";
Context context(true, parent_context.textclass);
context.layout = &parent_context.textclass.plainLayout();
if (use_minted && prefixIs(minted_nonfloat_caption, "[t]")) {
minted_nonfloat_caption.erase(0,3);
os << "\n\\begin_layout Plain Layout\n";
begin_inset(os, "Caption Standard\n");
Context newcontext(true, context.textclass,
context.layout, 0, context.font);
newcontext.check_layout(os);
os << minted_nonfloat_caption << "\n";
newcontext.check_end_layout(os);
end_inset(os);
os << "\n\\end_layout\n";
minted_nonfloat_caption.clear();
}
string s;
if (in_line) {
// set catcodes to verbatim early, just in case.
@ -1337,10 +1369,41 @@ void parse_listings(Parser & p, ostream & os, Context & parent_context, bool in_
//FIXME: handler error condition
s = p.verbatimStuff(delim).second;
// context.new_paragraph(os);
} else
} else if (use_minted) {
s = p.verbatimEnvironment("minted");
} else {
s = p.verbatimEnvironment("lstlisting");
}
output_ert(os, s, context);
end_inset(os);
if (use_minted && prefixIs(minted_nonfloat_caption, "[b]")) {
minted_nonfloat_caption.erase(0,3);
os << "\n\\begin_layout Plain Layout\n";
begin_inset(os, "Caption Standard\n");
Context newcontext(true, context.textclass,
context.layout, 0, context.font);
newcontext.check_layout(os);
os << minted_nonfloat_caption << "\n";
newcontext.check_end_layout(os);
end_inset(os);
os << "\n\\end_layout\n";
minted_nonfloat_caption.clear();
}
// Don't close the inset here for floating minted listings.
// It will be closed at the end of the listing environment.
if (!use_minted || minted_float.empty())
end_inset(os);
else {
eat_whitespace(p, os, parent_context, true);
Token t = p.get_token();
if (t.asInput() != "\\end") {
// If anything follows, collect it into a caption.
minted_float_has_caption = true;
os << "\n\\begin_layout Plain Layout\n"; // outer layout
begin_inset(os, "Caption Standard\n");
os << "\n\\begin_layout Plain Layout\n"; // inner layout
}
p.putback();
}
}
@ -1707,9 +1770,104 @@ void parse_environment(Parser & p, ostream & os, bool outer,
preamble.registerAutomaticallyLoadedPackage("framed");
}
else if (name == "lstlisting") {
else if (name == "listing") {
minted_float = "float";
eat_whitespace(p, os, parent_context, false);
parse_listings(p, os, parent_context, false);
string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
if (!opt.empty())
minted_float += "=" + opt;
// If something precedes \begin{minted}, we output it at the end
// as a caption, in order to keep it inside the listings inset.
eat_whitespace(p, os, parent_context, true);
p.pushPosition();
Token const & t = p.get_token();
p.skip_spaces(true);
string const envname = p.next_token().cat() == catBegin
? p.getArg('{', '}') : string();
bool prologue = t.asInput() != "\\begin" || envname != "minted";
p.popPosition();
minted_float_has_caption = false;
string content = parse_text_snippet(p, FLAG_END, outer,
parent_context);
size_t i = content.find("\\begin_inset listings");
bool minted_env = i != string::npos;
string caption;
if (prologue) {
caption = content.substr(0, i);
content.erase(0, i);
}
parent_context.check_layout(os);
if (minted_env && minted_float_has_caption) {
eat_whitespace(p, os, parent_context, true);
os << content << "\n";
if (!caption.empty())
os << caption << "\n";
os << "\n\\end_layout\n"; // close inner layout
end_inset(os); // close caption inset
os << "\n\\end_layout\n"; // close outer layout
} else if (!caption.empty()) {
if (!minted_env) {
begin_inset(os, "listings\n");
os << "lstparams " << '"' << minted_float << '"' << '\n';
os << "inline false\n";
os << "status collapsed\n";
}
os << "\n\\begin_layout Plain Layout\n";
begin_inset(os, "Caption Standard\n");
Context newcontext(true, parent_context.textclass,
0, 0, parent_context.font);
newcontext.check_layout(os);
os << caption << "\n";
newcontext.check_end_layout(os);
end_inset(os);
os << "\n\\end_layout\n";
} else if (content.empty()) {
begin_inset(os, "listings\n");
os << "lstparams " << '"' << minted_float << '"' << '\n';
os << "inline false\n";
os << "status collapsed\n";
} else {
os << content << "\n";
}
end_inset(os); // close listings inset
parent_context.check_end_layout(os);
parent_context.new_paragraph(os);
p.skip_spaces();
minted_float.clear();
minted_float_has_caption = false;
}
else if (name == "lstlisting" || name == "minted") {
bool use_minted = name == "minted";
eat_whitespace(p, os, parent_context, false);
if (use_minted && minted_float.empty()) {
// look ahead for a bottom caption
p.pushPosition();
bool found_end_minted = false;
while (!found_end_minted && p.good()) {
Token const & t = p.get_token();
p.skip_spaces();
string const envname =
p.next_token().cat() == catBegin
? p.getArg('{', '}') : string();
found_end_minted = t.asInput() == "\\end"
&& envname == "minted";
}
eat_whitespace(p, os, parent_context, true);
Token const & t = p.get_token();
p.skip_spaces(true);
if (t.asInput() == "\\lyxmintcaption") {
string const pos = p.getArg('[', ']');
if (pos == "b") {
string const caption =
parse_text_snippet(p, FLAG_ITEM,
false, parent_context);
minted_nonfloat_caption = "[b]" + caption;
}
}
p.popPosition();
}
parse_listings(p, os, parent_context, false, use_minted);
p.skip_spaces();
}
@ -2503,10 +2661,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
}
else if (p.isParagraph()) {
if (context.new_layout_allowed)
context.new_paragraph(os);
else
output_ert_inset(os, "\\par ", context);
// In minted floating listings we will collect
// everything into the caption, where multiple
// paragraphs are forbidden.
if (minted_float.empty()) {
if (context.new_layout_allowed)
context.new_paragraph(os);
else
output_ert_inset(os, "\\par ", context);
} else
os << ' ';
eat_whitespace(p, os, context, true);
}
@ -3165,9 +3329,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
end_inset(os);
}
else if (t.cs() == "lstinline") {
else if (t.cs() == "lstinline" || t.cs() == "mintinline") {
bool const use_minted = t.cs() == "mintinline";
p.skip_spaces();
parse_listings(p, os, context, true);
parse_listings(p, os, context, true, use_minted);
}
else if (t.cs() == "ensuremath") {
@ -3190,13 +3355,22 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
output_ert_inset(os, t.asInput(), context);
}
else if (t.cs() == "tableofcontents" || t.cs() == "lstlistoflistings") {
else if (t.cs() == "tableofcontents"
|| t.cs() == "lstlistoflistings"
|| t.cs() == "listoflistings") {
string name = t.cs();
if (preamble.minted() && name == "listoflistings")
name.insert(0, "lst");
context.check_layout(os);
begin_command_inset(os, "toc", t.cs());
begin_command_inset(os, "toc", name);
end_inset(os);
skip_spaces_braces(p);
if (t.cs() == "lstlistoflistings")
preamble.registerAutomaticallyLoadedPackage("listings");
if (name == "lstlistoflistings") {
if (preamble.minted())
preamble.registerAutomaticallyLoadedPackage("minted");
else
preamble.registerAutomaticallyLoadedPackage("listings");
}
}
else if (t.cs() == "listoffigures" || t.cs() == "listoftables") {
@ -3719,6 +3893,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
end_inset(os);
}
else if (t.cs() == "lyxmintcaption") {
string const pos = p.getArg('[', ']');
if (pos == "t") {
string const caption =
parse_text_snippet(p, FLAG_ITEM, false,
context);
minted_nonfloat_caption = "[t]" + caption;
} else {
// We already got the caption at the bottom,
// so simply skip it.
p.getArg('{', '}');
}
}
else if (t.cs() == "printindex" || t.cs() == "printsubindex") {
context.check_layout(os);
string commandname = t.cs();
@ -4613,14 +4801,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
docstring const name = newinsetlayout->name();
bool const caption = name.find(from_ascii("Caption:")) == 0;
if (caption) {
begin_inset(os, "Caption ");
os << to_utf8(name.substr(8)) << '\n';
// Already done for floating minted listings.
if (minted_float.empty()) {
begin_inset(os, "Caption ");
os << to_utf8(name.substr(8)) << '\n';
}
} else {
begin_inset(os, "Flex ");
os << to_utf8(name) << '\n'
<< "status collapsed\n";
}
if (newinsetlayout->isPassThru()) {
if (!minted_float.empty()) {
parse_text_snippet(p, os, FLAG_ITEM, false, context);
} else if (newinsetlayout->isPassThru()) {
// set catcodes to verbatim early, just in case.
p.setCatcodes(VERBATIM_CATCODES);
string delim = p.get_token().asInput();
@ -4636,7 +4829,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
if (caption)
p.skip_spaces();
end_inset(os);
// Minted caption insets are not closed here because
// we collect everything into the caption.
if (minted_float.empty())
end_inset(os);
}
else if (t.cs() == "includepdf") {