From ce5b420a44ec1bfa5898c05236cc16ccec210343 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Fri, 16 Mar 2018 12:56:17 +0100 Subject: [PATCH] tex2lyx: add support for LaTeXParam Part of #11068 (cherry picked from commit 506ae2d2ea1e646461841c57edd3f79c97d671c2) --- src/tex2lyx/Context.h | 2 + src/tex2lyx/Parser.cpp | 21 +++++++++ src/tex2lyx/Parser.h | 5 +++ src/tex2lyx/TODO.txt | 15 ------- src/tex2lyx/tex2lyx.cpp | 36 ++++++++------- src/tex2lyx/tex2lyx.h | 10 +++-- src/tex2lyx/text.cpp | 99 ++++++++++++++++++++++++++++++++++++++--- 7 files changed, 146 insertions(+), 42 deletions(-) diff --git a/src/tex2lyx/Context.h b/src/tex2lyx/Context.h index 47b39fc37c..e48bdf35d6 100644 --- a/src/tex2lyx/Context.h +++ b/src/tex2lyx/Context.h @@ -130,6 +130,8 @@ public: std::string par_extra_stuff; /// We may need to add something at the beginning of a list. std::string list_extra_stuff; + /// A LaTeXParam to be ignored in parsing. + std::string latexparam; /// If there has been an \\begin_deeper, we'll need a matching /// \\end_deeper bool need_end_deeper; diff --git a/src/tex2lyx/Parser.cpp b/src/tex2lyx/Parser.cpp index 1d506a78a8..94da955fea 100644 --- a/src/tex2lyx/Parser.cpp +++ b/src/tex2lyx/Parser.cpp @@ -650,6 +650,27 @@ string const Parser::plainCommand(char left, char right, string const & name) } +string const Parser::getCommandLatexParam() +{ + if (!good()) + return string(); + string res; + size_t offset = 0; + while (true) { + if (pos_ + offset >= tokens_.size()) + tokenize_one(); + if (pos_ + offset >= tokens_.size()) + break; + Token t = tokens_[pos_ + offset]; + if (t.cat() == catBegin) + break; + res += t.asInput(); + ++offset; + } + return res; +} + + Parser::Arg Parser::verbatimStuff(string const & end_string, bool const allow_linebreak) { if (!good()) diff --git a/src/tex2lyx/Parser.h b/src/tex2lyx/Parser.h index 83fa1c745a..b15f95aa41 100644 --- a/src/tex2lyx/Parser.h +++ b/src/tex2lyx/Parser.h @@ -278,6 +278,11 @@ public: * This function is designed to parse verbatim commands. */ std::string const plainCommand(char left, char right, std::string const & name); + /* + * Returns everything before the main command argument. + * This is where the LaTeXParam value of a layout is output. + */ + std::string const getCommandLatexParam(); /* * Basically the same as plainEnvironment() but the parsing is * stopped at string \p end_string. Contrary to the other diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index b42ea3fd28..be5d770021 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -45,7 +45,6 @@ Format LaTeX feature LyX feature 386 LyX version InsetInfo 390 forward/reverse search \forward_search, \forward_macro 391 decimal alignment in tables InsetTabular -392 new beamer format InsetLayout 399 automatic mathdots loading \use_mathdots 407 vertical offset for multirows InsetTabular 411 support for polyglossia \language_package (the cases of no package, of babel and of custom package is supported) @@ -53,21 +52,7 @@ Format LaTeX feature LyX feature 443 unicode-math.sty InsetMath* 448 453 automatic stmaryrd loading \use_package stmaryrd -454 beamer overprint environment InsetArgument, layout Overprint - \begin{overprint}[maxlength] - \onslide text ... - \end{overprint} -455 beamer frametitle command \begin_layout FrameTitle - \frametitle[short]{long} 457 automatic stackrel loading \use_package stackrel -466 Powerdot updates: - \pause[] layout Pause - \onslide{}{} InsetFlex, InsetArgument - \onslide*{}{} InsetFlex, InsetArgument - \onslide+{}{} InsetFlex, InsetArgument - \twocolumn[]{}{} Layout Twocolumn, InsetArgument - \item[]<> InsetArgument - \begin{enumerate|itemize|...}[] InsetArgument 526 Plural and capitalized refstyles InsetRef 533 Multibib support \begin{btUnit}...\end{btUnit} \multibib {none|part|chapter|section|subsection} diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index cbdd7500b6..416f1268f3 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -172,30 +172,32 @@ void add_known_theorem(string const & theorem, string const & o1, } -Layout const * findLayoutWithoutModule(TextClass const & textclass, - string const & name, bool command) +Layout const * findLayoutWithoutModule(TextClass const & tc, + string const & name, bool command, + string const & latexparam) { - DocumentClass::const_iterator it = textclass.begin(); - DocumentClass::const_iterator en = textclass.end(); - for (; it != en; ++it) { - if (it->latexname() == name && - ((command && it->isCommand()) || (!command && it->isEnvironment()))) - return &*it; + for (auto const & lay : tc) { + if (lay.latexname() == name && + (latexparam.empty() || + (!lay.latexparam().empty() && suffixIs(latexparam, lay.latexparam()))) && + ((command && lay.isCommand()) || (!command && lay.isEnvironment()))) + return &lay; } return 0; } -InsetLayout const * findInsetLayoutWithoutModule(TextClass const & textclass, - string const & name, bool command) +InsetLayout const * findInsetLayoutWithoutModule(TextClass const & tc, + string const & name, bool command, + string const & latexparam) { - DocumentClass::InsetLayouts::const_iterator it = textclass.insetLayouts().begin(); - DocumentClass::InsetLayouts::const_iterator en = textclass.insetLayouts().end(); - for (; it != en; ++it) { - if (it->second.latexname() == name && - ((command && it->second.latextype() == InsetLayout::COMMAND) || - (!command && it->second.latextype() == InsetLayout::ENVIRONMENT))) - return &(it->second); + for (auto const & ilay : tc.insetLayouts()) { + if (ilay.second.latexname() == name && + (latexparam.empty() || + (!ilay.second.latexparam().empty() && suffixIs(latexparam, ilay.second.latexparam()))) && + ((command && ilay.second.latextype() == InsetLayout::COMMAND) || + (!command && ilay.second.latextype() == InsetLayout::ENVIRONMENT))) + return &(ilay.second); } return 0; } diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 34da5902b0..1791c2f414 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -112,10 +112,12 @@ extern void add_known_environment(std::string const & environment, docstring const & end); extern void add_known_theorem(std::string const & theorem, std::string const & o1, bool o2, docstring const & definition); -extern Layout const * findLayoutWithoutModule(TextClass const & textclass, - std::string const & name, bool command); -extern InsetLayout const * findInsetLayoutWithoutModule( - TextClass const & textclass, std::string const & name, bool command); +extern Layout const * findLayoutWithoutModule(TextClass const & tc, + std::string const & name, bool command, + std::string const & latexparam = std::string()); +extern InsetLayout const * findInsetLayoutWithoutModule(TextClass const & tc, + std::string const & name, bool command, + std::string const & latexparam = std::string()); /*! * Check whether a module provides command (if \p command is true) or * environment (if \p command is false) \p name, and add the module to the diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index f3617a8e97..2036ea612c 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -67,6 +67,13 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer, if (layout) output_arguments(os, p, outer, false, string(), newcontext, layout->latexargs()); + // If we have a latex param, we eat it here. + if (!context.latexparam.empty()) { + ostringstream oss; + Context dummy(true, context.textclass); + parse_text(p, oss, FLAG_RDELIM, outer, dummy, + string(1, context.latexparam.back())); + } parse_text(p, os, flags, outer, newcontext, rdelim); if (layout) output_arguments(os, p, outer, false, "post", newcontext, @@ -697,24 +704,27 @@ void output_comment(Parser & p, ostream & os, string const & s, } -Layout const * findLayout(TextClass const & textclass, string const & name, bool command) +Layout const * findLayout(TextClass const & textclass, string const & name, bool command, + string const & latexparam = string()) { - Layout const * layout = findLayoutWithoutModule(textclass, name, command); + Layout const * layout = findLayoutWithoutModule(textclass, name, command, latexparam); if (layout) return layout; if (checkModule(name, command)) - return findLayoutWithoutModule(textclass, name, command); + return findLayoutWithoutModule(textclass, name, command, latexparam); return layout; } -InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command) +InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command, + string const & latexparam = string()) { - InsetLayout const * insetlayout = findInsetLayoutWithoutModule(textclass, name, command); + InsetLayout const * insetlayout = + findInsetLayoutWithoutModule(textclass, name, command, latexparam); if (insetlayout) return insetlayout; if (checkModule(name, command)) - return findInsetLayoutWithoutModule(textclass, name, command); + return findInsetLayoutWithoutModule(textclass, name, command, latexparam); return insetlayout; } @@ -844,6 +854,13 @@ void output_command_layout(ostream & os, Parser & p, bool outer, context.check_deeper(os); output_arguments(os, p, outer, true, string(), context, context.layout->latexargs()); + // If we have a latex param, we eat it here. + if (!parent_context.latexparam.empty()) { + ostringstream oss; + Context dummy(true, parent_context.textclass); + parse_text(p, oss, FLAG_RDELIM, outer, dummy, + string(1, parent_context.latexparam.back())); + } parse_text(p, os, FLAG_ITEM, outer, context); output_arguments(os, p, outer, false, "post", context, context.layout->postcommandargs()); @@ -3269,6 +3286,25 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, continue; } + // Before we look for the layout name with star and alone below, we check the layouts including + // the LateXParam, which might be one or several options or a star. + // The single '=' is meant here. + if (context.new_layout_allowed && + (newlayout = findLayout(context.textclass, t.cs(), true, p.getCommandLatexParam()))) { + // store the latexparam here. This is eaten in output_command_layout + context.latexparam = newlayout->latexparam(); + // write the layout + output_command_layout(os, p, outer, context, newlayout); + p.skip_spaces(); + if (!preamble.titleLayoutFound()) + preamble.titleLayoutFound(newlayout->intitle); + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); it != req.end(); ++it) + preamble.registerAutomaticallyLoadedPackage(*it); + continue; + } + + // Starred section headings // Must attempt to parse "Section*" before "Section". if ((p.next_token().asInput() == "*") && @@ -5389,6 +5425,57 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, continue; } + // Before we look for the layout name alone below, we check the layouts including the LateXParam, which + // might be one or several options or a star. + // The single '=' is meant here. + if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true, p.getCommandLatexParam()))) { + if (starred) + p.get_token(); + p.skip_spaces(); + context.check_layout(os); + // store the latexparam here. This is eaten in parse_text_in_inset + context.latexparam = newinsetlayout->latexparam(); + docstring name = newinsetlayout->name(); + bool const caption = name.find(from_ascii("Caption:")) == 0; + if (caption) { + // Already done for floating minted listings. + if (minted_float.empty()) { + begin_inset(os, "Caption "); + os << to_utf8(name.substr(8)) << '\n'; + } + } else { + // FIXME: what do we do if the prefix is not Flex: ? + if (prefixIs(name, from_ascii("Flex:"))) + name.erase(0, 5); + begin_inset(os, "Flex "); + os << to_utf8(name) << '\n' + << "status collapsed\n"; + } + 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(); + if (delim != "{") + cerr << "Warning: bad delimiter for command " << t.asInput() << endl; + //FIXME: handle error condition + string const arg = p.verbatimStuff("}").second; + Context newcontext(true, context.textclass); + if (newinsetlayout->forcePlainLayout()) + newcontext.layout = &context.textclass.plainLayout(); + output_ert(os, arg, newcontext); + } else + parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout); + if (caption) + p.skip_spaces(); + // Minted caption insets are not closed here because + // we collect everything into the caption. + if (minted_float.empty()) + end_inset(os); + continue; + } + // The single '=' is meant here. if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true))) { if (starred)