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); - 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") {