From: Enrico Forestieri Date: Sat, 17 Jun 2017 00:23:00 +0000 (+0200) Subject: tex2lyx: import minted listings X-Git-Tag: 2.3.0beta1~237 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=7a9bb851840fe7211cac05cbd898b27143f73559;p=features.git 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. --- diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 6324a91c79..6650cf3fbb 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -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_seppackage_mid_seppackage_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()) diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h index 342cc68734..5dcc6718d7 100644 --- a/src/tex2lyx/Preamble.h +++ b/src/tex2lyx/Preamble.h @@ -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. diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index 327420dbfd..2c835dd64c 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -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}?...? diff --git a/src/tex2lyx/test/CJK.lyx.lyx b/src/tex2lyx/test/CJK.lyx.lyx index 82bbba3171..59d3729c3a 100644 --- a/src/tex2lyx/test/CJK.lyx.lyx +++ b/src/tex2lyx/test/CJK.lyx.lyx @@ -63,6 +63,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/CJKutf8.lyx.lyx b/src/tex2lyx/test/CJKutf8.lyx.lyx index 7e3dbc883e..7eeec41b14 100644 --- a/src/tex2lyx/test/CJKutf8.lyx.lyx +++ b/src/tex2lyx/test/CJKutf8.lyx.lyx @@ -63,6 +63,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/DummyDocument.lyx.lyx b/src/tex2lyx/test/DummyDocument.lyx.lyx index 86e20d489b..03a67e005a 100644 --- a/src/tex2lyx/test/DummyDocument.lyx.lyx +++ b/src/tex2lyx/test/DummyDocument.lyx.lyx @@ -61,6 +61,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/Dummy~Document.lyx.lyx b/src/tex2lyx/test/Dummy~Document.lyx.lyx index 79bf881a68..30196892b0 100644 --- a/src/tex2lyx/test/Dummy~Document.lyx.lyx +++ b/src/tex2lyx/test/Dummy~Document.lyx.lyx @@ -61,6 +61,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx b/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx index 8162a985f9..fac9a9573a 100644 --- a/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx +++ b/src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx @@ -62,6 +62,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/algo2e.lyx.lyx b/src/tex2lyx/test/algo2e.lyx.lyx index 39e9689787..fa60631d38 100644 --- a/src/tex2lyx/test/algo2e.lyx.lyx +++ b/src/tex2lyx/test/algo2e.lyx.lyx @@ -61,6 +61,7 @@ algorithm2e \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/box-color-size-space-align.lyx.lyx b/src/tex2lyx/test/box-color-size-space-align.lyx.lyx index d399999a5d..30a2b15551 100644 --- a/src/tex2lyx/test/box-color-size-space-align.lyx.lyx +++ b/src/tex2lyx/test/box-color-size-space-align.lyx.lyx @@ -87,6 +87,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \notefontcolor #0000ff \backgroundcolor #ff5500 \boxbgcolor #ffff00 diff --git a/src/tex2lyx/test/test-insets-basic.lyx.lyx b/src/tex2lyx/test/test-insets-basic.lyx.lyx index 0862f8a915..3e29da2602 100644 --- a/src/tex2lyx/test/test-insets-basic.lyx.lyx +++ b/src/tex2lyx/test/test-insets-basic.lyx.lyx @@ -97,6 +97,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-insets.lyx.lyx b/src/tex2lyx/test/test-insets.lyx.lyx index 42fa3e628f..0aa2ee886a 100644 --- a/src/tex2lyx/test/test-insets.lyx.lyx +++ b/src/tex2lyx/test/test-insets.lyx.lyx @@ -73,6 +73,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-memoir.lyx.lyx b/src/tex2lyx/test/test-memoir.lyx.lyx index e1dc00b824..d777c5e4f9 100644 --- a/src/tex2lyx/test/test-memoir.lyx.lyx +++ b/src/tex2lyx/test/test-memoir.lyx.lyx @@ -59,6 +59,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-modules.lyx.lyx b/src/tex2lyx/test/test-modules.lyx.lyx index 9f754c4661..034010a454 100644 --- a/src/tex2lyx/test/test-modules.lyx.lyx +++ b/src/tex2lyx/test/test-modules.lyx.lyx @@ -59,6 +59,7 @@ theorems-ams \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-refstyle-theorems.lyx.lyx b/src/tex2lyx/test/test-refstyle-theorems.lyx.lyx index fa573b6ff6..0e5491f750 100644 --- a/src/tex2lyx/test/test-refstyle-theorems.lyx.lyx +++ b/src/tex2lyx/test/test-refstyle-theorems.lyx.lyx @@ -59,6 +59,7 @@ theorems-ams \suppress_date false \justification true \use_refstyle 1 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-scr.lyx.lyx b/src/tex2lyx/test/test-scr.lyx.lyx index 35f494649e..61d3fd80c3 100644 --- a/src/tex2lyx/test/test-scr.lyx.lyx +++ b/src/tex2lyx/test/test-scr.lyx.lyx @@ -56,6 +56,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test-structure.lyx.lyx b/src/tex2lyx/test/test-structure.lyx.lyx index 1ec7c69c70..b1cb6808a8 100644 --- a/src/tex2lyx/test/test-structure.lyx.lyx +++ b/src/tex2lyx/test/test-structure.lyx.lyx @@ -92,6 +92,7 @@ logicalmkup \suppress_date true \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/test.lyx.lyx b/src/tex2lyx/test/test.lyx.lyx index 31e21063d3..99ed5a4d0f 100644 --- a/src/tex2lyx/test/test.lyx.lyx +++ b/src/tex2lyx/test/test.lyx.lyx @@ -63,6 +63,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/test/verbatim.lyx.lyx b/src/tex2lyx/test/verbatim.lyx.lyx index ba3c82ec7a..569473260e 100644 --- a/src/tex2lyx/test/verbatim.lyx.lyx +++ b/src/tex2lyx/test/verbatim.lyx.lyx @@ -52,6 +52,7 @@ \suppress_date false \justification true \use_refstyle 0 +\use_minted 0 \index Index \shortcut idx \color #008000 diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index 811883e9f1..af2abeb82f 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -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 & res, vector & 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); + 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); - parse_listings(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") {