]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
Fix more warnings and simplify a tiny bit.
[lyx.git] / src / tex2lyx / text.cpp
index 9ef22fb03f2b09c0947c7775267427b42ce1a2f4..f9201cd1a26f73f307979729869a3f243d5d0683 100644 (file)
@@ -47,14 +47,15 @@ namespace lyx {
 
 namespace {
 
-void output_arguments(ostream &, Parser &, bool, bool, bool, Context &,
+void output_arguments(ostream &, Parser &, bool, bool, string, Context &,
                       Layout::LaTeXArgMap const &);
 
 }
 
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context const & context, InsetLayout const * layout)
+               Context const & context, InsetLayout const * layout,
+               string const rdelim)
 {
        bool const forcePlainLayout =
                layout ? layout->forcePlainLayout() : false;
@@ -64,11 +65,18 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
        else
                newcontext.font = context.font;
        if (layout)
-               output_arguments(os, p, outer, false, false, newcontext,
+               output_arguments(os, p, outer, false, string(), newcontext,
                                 layout->latexargs());
-       parse_text(p, os, flags, outer, newcontext);
+       // 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, true, newcontext,
+               output_arguments(os, p, outer, false, "post", newcontext,
                                 layout->postcommandargs());
        newcontext.check_end_layout(os);
 }
@@ -77,14 +85,15 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
 namespace {
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context const & context, string const & name)
+               Context const & context, string const & name,
+               string const rdelim = string())
 {
        InsetLayout const * layout = 0;
        DocumentClass::InsetLayouts::const_iterator it =
                context.textclass.insetLayouts().find(from_ascii(name));
        if (it != context.textclass.insetLayouts().end())
                layout = &(it->second);
-       parse_text_in_inset(p, os, flags, outer, context, layout);
+       parse_text_in_inset(p, os, flags, outer, context, layout, rdelim);
 }
 
 /// parses a paragraph snippet, useful for example for \\emph{...}
@@ -201,13 +210,14 @@ bool need_commentbib = false;
 char const * const known_quotes[] = { "dq", "guillemotleft", "flqq", "og",
 "guillemotright", "frqq", "fg", "glq", "glqq", "textquoteleft", "grq", "grqq",
 "quotedblbase", "textquotedblleft", "quotesinglbase", "textquoteright", "flq",
-"guilsinglleft", "frq", "guilsinglright", 0};
+"guilsinglleft", "frq", "guilsinglright", "textquotedblright", "textquotesingle",
+"textquotedbl", 0};
 
 /// the same as known_quotes with .lyx names
-char const * const known_coded_quotes[] = { "prd", "ard", "ard", "ard",
-"ald", "ald", "ald", "gls", "gld", "els", "els", "grd",
-"gld", "grd", "gls", "ers", "fls",
-"fls", "frs", "frs", 0};
+char const * const known_coded_quotes[] = { "qrd", "ard", "ard", "ard",
+"ald", "ald", "ald", "gls", "gld", "els", "els", "eld",
+"gld", "eld", "gls", "ers", "ars",
+"ars", "als", "als", "erd", "qrs", "qrd", 0};
 
 /// LaTeX names for font sizes
 char const * const known_sizes[] = { "tiny", "scriptsize", "footnotesize",
@@ -345,7 +355,7 @@ string minted_nonfloat_caption = "";
 
 // Characters that have to be escaped by \\ in LaTeX
 char const * const known_escaped_chars[] = {
-               "&", "_", "$", "%", "#", "^", "{", "}"};
+               "&", "_", "$", "%", "#", "^", "{", "}", 0};
 
 
 /// splits "x=z, y=b" into a map and an ordered keyword vector
@@ -446,6 +456,107 @@ bool translate_len(string const & length, string & valstring, string & unit)
        return true;
 }
 
+
+/// If we have ambiguous quotation marks, make a smart guess
+/// based on main quote style
+string guessQuoteStyle(string in, bool const opening)
+{
+       string res = in;
+       if (prefixIs(in, "qr")) {// straight quote
+               if (!opening)
+                       res = subst(res, "r", "l");
+       } else if (in == "eld") {// ``
+               if (preamble.quotesStyle() == "german")
+                       res = "grd";
+               else if (preamble.quotesStyle() == "british")
+                       res = "bls";
+               else if (preamble.quotesStyle() == "french")
+                       res = "fls";
+               else if (preamble.quotesStyle() == "russian")
+                       res = "rrs";
+       } else if (in == "erd") {// ''
+               if (preamble.quotesStyle() == "polish")
+                       res = "prd";
+               else if (preamble.quotesStyle() == "british")
+                       res = "brs";
+               else if (preamble.quotesStyle() == "french")
+                       res = "frs";
+               else if (preamble.quotesStyle() == "swedish")
+                       res = opening ? "sld" : "srd";
+       } else if (in == "els") {// `
+               if (preamble.quotesStyle() == "german")
+                       res = "grs";
+               else if (preamble.quotesStyle() == "british")
+                       res = "bld";
+       } else if (in == "ers") {// '
+               if (preamble.quotesStyle() == "polish")
+                       res = "prs";
+               else if (preamble.quotesStyle() == "british")
+                       res = "brd";
+               else if (preamble.quotesStyle() == "swedish")
+                       res = opening ? "sls" : "srs";
+       } else if (in == "ard") {// >>
+               if (preamble.quotesStyle() == "swiss")
+                       res = "cld";
+               else if (preamble.quotesStyle() == "french")
+                       res = "fld";
+               else if (preamble.quotesStyle() == "russian")
+                       res = "rld";
+       } else if (in == "ald") {// <<
+               if (preamble.quotesStyle() == "swiss")
+                       res = "crd";
+               else if (preamble.quotesStyle() == "french")
+                       res = "frd";
+               else if (preamble.quotesStyle() == "russian")
+                       res = "rrd";
+       } else if (in == "ars") {// >
+               if (preamble.quotesStyle() == "swiss")
+                       res = "cls";
+       } else if (in == "als") {// <
+               if (preamble.quotesStyle() == "swiss")
+                       res = "crs";
+       } else if (in == "gld") {// ,,
+               if (preamble.quotesStyle() == "polish")
+                       res = "pld";
+               else if (preamble.quotesStyle() == "russian")
+                       res = "rls";
+       } else if (in == "gls") {// ,
+               if (preamble.quotesStyle() == "polish")
+                       res = "pls";
+       }
+       return res;
+}
+
+
+string const fromPolyglossiaEnvironment(string const s)
+{
+       // Since \arabic is taken by the LaTeX kernel,
+       // the Arabic polyglossia environment is upcased
+       if (s == "Arabic")
+               return "arabic";
+       else
+               return s;
+}
+
+
+string uncapitalize(string const s)
+{
+       docstring in = from_ascii(s);
+       char_type t = lowercase(s[0]);
+       in[0] = t;
+       return to_ascii(in);
+}
+
+
+bool isCapitalized(string const s)
+{
+       docstring in = from_ascii(s);
+       char_type t = uppercase(s[0]);
+       in[0] = t;
+       return to_ascii(in) == s;
+}
+
+
 } // namespace
 
 
@@ -548,7 +659,7 @@ pair<bool, docstring> convert_unicodesymbols(docstring s)
                else {
                        res = false;
                        for (auto const & c : known_escaped_chars)
-                               if (prefixIs(s, from_ascii("\\") + c))
+                               if (c != 0 && prefixIs(s, from_ascii("\\") + c))
                                        res = true;
                        i = 1;
                }
@@ -622,29 +733,33 @@ 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;
 }
 
 
-void eat_whitespace(Parser &, ostream &, Context &, bool);
+void eat_whitespace(Parser &, ostream &, Context &, bool eatParagraph,
+                   bool eatNewline = true);
 
 
 /*!
@@ -674,14 +789,16 @@ void skip_spaces_braces(Parser & p, bool keepws = false)
 }
 
 
-void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, bool post,
+void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, string const prefix,
                       Context & context, Layout::LaTeXArgMap const & latexargs)
 {
-       if (need_layout) {
-               context.check_layout(os);
-               need_layout = false;
-       } else
-               need_layout = true;
+       if (context.layout->latextype != LATEX_ITEM_ENVIRONMENT || !prefix.empty()) {
+               if (need_layout) {
+                       context.check_layout(os);
+                       need_layout = false;
+               } else
+                       need_layout = true;
+       }
        int i = 0;
        Layout::LaTeXArgMap::const_iterator lait = latexargs.begin();
        Layout::LaTeXArgMap::const_iterator const laend = latexargs.end();
@@ -691,31 +808,50 @@ void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, bo
                if (lait->second.mandatory) {
                        if (p.next_token().cat() != catBegin)
                                break;
-                       p.get_token(); // eat '{'
+                       string ldelim = to_utf8(lait->second.ldelim);
+                       string rdelim = to_utf8(lait->second.rdelim);
+                       if (ldelim.empty())
+                               ldelim = "{";
+                       if (rdelim.empty())
+                               rdelim = "}";
+                       p.get_token(); // eat ldelim
+                       if (ldelim.size() > 1)
+                               p.get_token(); // eat ldelim
                        if (need_layout) {
                                context.check_layout(os);
                                need_layout = false;
                        }
                        begin_inset(os, "Argument ");
-                       if (post)
-                               os << "post:";
+                       if (!prefix.empty())
+                               os << prefix << ':';
                        os << i << "\nstatus collapsed\n\n";
-                       parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
+                       parse_text_in_inset(p, os, FLAG_RDELIM, outer, context, 0, rdelim);
                        end_inset(os);
                } else {
-                       if (p.next_token().cat() == catEscape ||
-                           p.next_token().character() != '[')
+                       string ldelim = to_utf8(lait->second.ldelim);
+                       string rdelim = to_utf8(lait->second.rdelim);
+                       if (ldelim.empty())
+                               ldelim = "[";
+                       if (rdelim.empty())
+                               rdelim = "]";
+                       string tok = p.next_token().asInput();
+                       // we only support delimiters with max 2 chars for now.
+                       if (ldelim.size() > 1)
+                               tok += p.next_next_token().asInput();
+                       if (p.next_token().cat() == catEscape || tok != ldelim)
                                continue;
-                       p.get_token(); // eat '['
+                       p.get_token(); // eat ldelim
+                       if (ldelim.size() > 1)
+                               p.get_token(); // eat ldelim
                        if (need_layout) {
                                context.check_layout(os);
                                need_layout = false;
                        }
                        begin_inset(os, "Argument ");
-                       if (post)
-                               os << "post:";
+                       if (!prefix.empty())
+                               os << prefix << ':';
                        os << i << "\nstatus collapsed\n\n";
-                       parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
+                       parse_text_in_inset(p, os, FLAG_RDELIM, outer, context, 0, rdelim);
                        end_inset(os);
                }
                eat_whitespace(p, os, context, false);
@@ -746,10 +882,17 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                context.need_end_deeper = true;
        }
        context.check_deeper(os);
-       output_arguments(os, p, outer, true, false, context,
+       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, true, context,
+       output_arguments(os, p, outer, false, "post", context,
                         context.layout->postcommandargs());
        context.check_end_layout(os);
        if (parent_context.deeper_paragraph) {
@@ -1370,7 +1513,7 @@ void parse_listings(Parser & p, ostream & os, Context & parent_context,
                os << "inline true\n";
        else
                os << "inline false\n";
-       os << "status collapsed\n";
+       os << "status open\n";
        Context context(true, parent_context.textclass);
        context.layout = &parent_context.textclass.plainLayout();
        if (use_minted && prefixIs(minted_nonfloat_caption, "[t]")) {
@@ -1452,6 +1595,14 @@ void parse_unknown_environment(Parser & p, string const & name, ostream & os,
        if (specialfont)
                parent_context.new_layout_allowed = false;
        output_ert_inset(os, "\\begin{" + name + "}", parent_context);
+       // Try to handle options: Look if we have optional arguments,
+       // and if so, put the brackets in ERT.
+       while (p.hasOpt()) {
+               p.get_token(); // eat '['
+               output_ert_inset(os, "[", parent_context);
+               os << parse_text_snippet(p, FLAG_BRACK_LAST, outer, parent_context);
+               output_ert_inset(os, "]", parent_context);
+       }
        parse_text_snippet(p, os, flags, outer, parent_context);
        output_ert_inset(os, "\\end{" + name + "}", parent_context);
        if (specialfont)
@@ -1469,621 +1620,821 @@ void parse_environment(Parser & p, ostream & os, bool outer,
        string const unstarred_name = rtrim(name, "*");
        active_environments.push_back(name);
 
-       if (is_math_env(name)) {
-               parent_context.check_layout(os);
-               begin_inset(os, "Formula ");
-               os << "\\begin{" << name << "}";
-               parse_math(p, os, FLAG_END, MATH_MODE);
-               os << "\\end{" << name << "}";
-               end_inset(os);
-               if (is_display_math_env(name)) {
-                       // Prevent the conversion of a line break to a space
-                       // (bug 7668). This does not change the output, but
-                       // looks ugly in LyX.
-                       eat_whitespace(p, os, parent_context, false);
+       // We use this loop and break out after a condition is met
+       // rather than a huge else-if-chain.
+       while (true) {
+               if (is_math_env(name)) {
+                       parent_context.check_layout(os);
+                       begin_inset(os, "Formula ");
+                       os << "\\begin{" << name << "}";
+                       parse_math(p, os, FLAG_END, MATH_MODE);
+                       os << "\\end{" << name << "}";
+                       end_inset(os);
+                       if (is_display_math_env(name)) {
+                               // Prevent the conversion of a line break to a space
+                               // (bug 7668). This does not change the output, but
+                               // looks ugly in LyX.
+                               eat_whitespace(p, os, parent_context, false);
+                       }
+                       break;
                }
-       }
 
-       else if (is_known(name, preamble.polyglossia_languages)) {
-               // We must begin a new paragraph if not already done
-               if (! parent_context.atParagraphStart()) {
-                       parent_context.check_end_layout(os);
+               // We need to use fromPolyglossiaEnvironment die to Arabic > arabic
+               if (is_known(fromPolyglossiaEnvironment(name), preamble.polyglossia_languages)) {
+                       // We must begin a new paragraph if not already done
+                       if (! parent_context.atParagraphStart()) {
+                               parent_context.check_end_layout(os);
+                               parent_context.new_paragraph(os);
+                       }
+                       // save the language in the context so that it is
+                       // handled by parse_text
+                       parent_context.font.language =
+                               preamble.polyglossia2lyx(fromPolyglossiaEnvironment(name));
+                       parse_text(p, os, FLAG_END, outer, parent_context);
+                       // Just in case the environment is empty
+                       parent_context.extra_stuff.erase();
+                       // We must begin a new paragraph to reset the language
                        parent_context.new_paragraph(os);
+                       p.skip_spaces();
+                       break;
                }
-               // save the language in the context so that it is
-               // handled by parse_text
-               parent_context.font.language = preamble.polyglossia2lyx(name);
-               parse_text(p, os, FLAG_END, outer, parent_context);
-               // Just in case the environment is empty
-               parent_context.extra_stuff.erase();
-               // We must begin a new paragraph to reset the language
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-       }
 
-       else if (unstarred_name == "tabular" || name == "longtable") {
-               eat_whitespace(p, os, parent_context, false);
-               string width = "0pt";
-               if (name == "tabular*") {
-                       width = lyx::translate_len(p.getArg('{', '}'));
+               if (unstarred_name == "tabular" || name == "longtable"
+                        || name == "tabularx" || name == "xltabular") {
                        eat_whitespace(p, os, parent_context, false);
+                       string width = "0pt";
+                       string halign;
+                       if ((name == "longtable" || name == "xltabular") && p.hasOpt()) {
+                               string const opt = p.getArg('[', ']');
+                               if (opt == "c")
+                                       halign = "center";
+                               else if (opt == "l")
+                                       halign = "left";
+                               else if (opt == "r")
+                                       halign = "right";
+                       }
+                       if (name == "tabular*" || name == "tabularx" || name == "xltabular") {
+                               width = lyx::translate_len(p.getArg('{', '}'));
+                               eat_whitespace(p, os, parent_context, false);
+                       }
+                       parent_context.check_layout(os);
+                       begin_inset(os, "Tabular ");
+                       handle_tabular(p, os, name, width, halign, parent_context);
+                       end_inset(os);
+                       p.skip_spaces();
+                       break;
                }
-               parent_context.check_layout(os);
-               begin_inset(os, "Tabular ");
-               handle_tabular(p, os, name, width, parent_context);
-               end_inset(os);
-               p.skip_spaces();
-       }
 
-       else if (parent_context.textclass.floats().typeExist(unstarred_name)) {
-               eat_whitespace(p, os, parent_context, false);
-               string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Float " + unstarred_name + "\n");
-               // store the float type for subfloats
-               // subfloats only work with figures and tables
-               if (unstarred_name == "figure")
-                       float_type = unstarred_name;
-               else if (unstarred_name == "table")
-                       float_type = unstarred_name;
-               else
-                       float_type = "";
-               if (!opt.empty())
-                       os << "placement " << opt << '\n';
-               if (contains(opt, "H"))
-                       preamble.registerAutomaticallyLoadedPackage("float");
-               else {
-                       Floating const & fl = parent_context.textclass.floats()
-                                             .getType(unstarred_name);
-                       if (!fl.floattype().empty() && fl.usesFloatPkg())
+               if (parent_context.textclass.floats().typeExist(unstarred_name)) {
+                       eat_whitespace(p, os, parent_context, false);
+                       string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       begin_inset(os, "Float " + unstarred_name + "\n");
+                       // store the float type for subfloats
+                       // subfloats only work with figures and tables
+                       if (unstarred_name == "figure")
+                               float_type = unstarred_name;
+                       else if (unstarred_name == "table")
+                               float_type = unstarred_name;
+                       else
+                               float_type = "";
+                       if (!opt.empty())
+                               os << "placement " << opt << '\n';
+                       if (contains(opt, "H"))
                                preamble.registerAutomaticallyLoadedPackage("float");
-               }
+                       else {
+                               Floating const & fl = parent_context.textclass.floats()
+                                                     .getType(unstarred_name);
+                               if (!fl.floattype().empty() && fl.usesFloatPkg())
+                                       preamble.registerAutomaticallyLoadedPackage("float");
+                       }
 
-               os << "wide " << convert<string>(is_starred)
-                  << "\nsideways false"
-                  << "\nstatus open\n\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               // We don't need really a new paragraph, but
-               // we must make sure that the next item gets a \begin_layout.
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-               // the float is parsed thus delete the type
-               float_type = "";
-       }
+                       os << "wide " << convert<string>(is_starred)
+                          << "\nsideways false"
+                          << "\nstatus open\n\n";
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       // We don't need really a new paragraph, but
+                       // we must make sure that the next item gets a \begin_layout.
+                       parent_context.new_paragraph(os);
+                       p.skip_spaces();
+                       // the float is parsed thus delete the type
+                       float_type = "";
+                       break;
+               }
 
-       else if (unstarred_name == "sidewaysfigure"
-               || unstarred_name == "sidewaystable"
-               || unstarred_name == "sidewaysalgorithm") {
-               string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               if (unstarred_name == "sidewaysfigure")
-                       begin_inset(os, "Float figure\n");
-               else if (unstarred_name == "sidewaystable")
-                       begin_inset(os, "Float table\n");
-               else if (unstarred_name == "sidewaysalgorithm")
-                       begin_inset(os, "Float algorithm\n");
-               if (!opt.empty())
-                       os << "placement " << opt << '\n';
-               if (contains(opt, "H"))
-                       preamble.registerAutomaticallyLoadedPackage("float");
-               os << "wide " << convert<string>(is_starred)
-                  << "\nsideways true"
-                  << "\nstatus open\n\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               // We don't need really a new paragraph, but
-               // we must make sure that the next item gets a \begin_layout.
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-               preamble.registerAutomaticallyLoadedPackage("rotfloat");
-       }
+               if (unstarred_name == "sidewaysfigure"
+                   || unstarred_name == "sidewaystable"
+                   || unstarred_name == "sidewaysalgorithm") {
+                       string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       if (unstarred_name == "sidewaysfigure")
+                               begin_inset(os, "Float figure\n");
+                       else if (unstarred_name == "sidewaystable")
+                               begin_inset(os, "Float table\n");
+                       else if (unstarred_name == "sidewaysalgorithm")
+                               begin_inset(os, "Float algorithm\n");
+                       if (!opt.empty())
+                               os << "placement " << opt << '\n';
+                       if (contains(opt, "H"))
+                               preamble.registerAutomaticallyLoadedPackage("float");
+                       os << "wide " << convert<string>(is_starred)
+                          << "\nsideways true"
+                          << "\nstatus open\n\n";
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       // We don't need really a new paragraph, but
+                       // we must make sure that the next item gets a \begin_layout.
+                       parent_context.new_paragraph(os);
+                       p.skip_spaces();
+                       preamble.registerAutomaticallyLoadedPackage("rotfloat");
+                       break;
+               }
 
-       else if (name == "wrapfigure" || name == "wraptable") {
-               // syntax is \begin{wrapfigure}[lines]{placement}[overhang]{width}
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               // default values
-               string lines = "0";
-               string overhang = "0col%";
-               // parse
-               if (p.hasOpt())
-                       lines = p.getArg('[', ']');
-               string const placement = p.getArg('{', '}');
-               if (p.hasOpt())
-                       overhang = p.getArg('[', ']');
-               string const width = p.getArg('{', '}');
-               // write
-               if (name == "wrapfigure")
-                       begin_inset(os, "Wrap figure\n");
-               else
-                       begin_inset(os, "Wrap table\n");
-               os << "lines " << lines
-                  << "\nplacement " << placement
-                  << "\noverhang " << lyx::translate_len(overhang)
-                  << "\nwidth " << lyx::translate_len(width)
-                  << "\nstatus open\n\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               // We don't need really a new paragraph, but
-               // we must make sure that the next item gets a \begin_layout.
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-               preamble.registerAutomaticallyLoadedPackage("wrapfig");
-       }
+               if (name == "wrapfigure" || name == "wraptable") {
+                       // syntax is \begin{wrapfigure}[lines]{placement}[overhang]{width}
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       // default values
+                       string lines = "0";
+                       string overhang = "0col%";
+                       // parse
+                       if (p.hasOpt())
+                               lines = p.getArg('[', ']');
+                       string const placement = p.getArg('{', '}');
+                       if (p.hasOpt())
+                               overhang = p.getArg('[', ']');
+                       string const width = p.getArg('{', '}');
+                       // write
+                       if (name == "wrapfigure")
+                               begin_inset(os, "Wrap figure\n");
+                       else
+                               begin_inset(os, "Wrap table\n");
+                       os << "lines " << lines
+                          << "\nplacement " << placement
+                          << "\noverhang " << lyx::translate_len(overhang)
+                          << "\nwidth " << lyx::translate_len(width)
+                          << "\nstatus open\n\n";
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       // We don't need really a new paragraph, but
+                       // we must make sure that the next item gets a \begin_layout.
+                       parent_context.new_paragraph(os);
+                       p.skip_spaces();
+                       preamble.registerAutomaticallyLoadedPackage("wrapfig");
+                       break;
+               }
 
-       else if (name == "minipage") {
-               eat_whitespace(p, os, parent_context, false);
-               // Test whether this is an outer box of a shaded box
-               p.pushPosition();
-               // swallow arguments
-               while (p.hasOpt()) {
-                       p.getArg('[', ']');
+               if (name == "minipage") {
+                       eat_whitespace(p, os, parent_context, false);
+                       // Test whether this is an outer box of a shaded box
+                       p.pushPosition();
+                       // swallow arguments
+                       while (p.hasOpt()) {
+                               p.getArg('[', ']');
+                               p.skip_spaces(true);
+                       }
+                       p.getArg('{', '}');
                        p.skip_spaces(true);
+                       Token t = p.get_token();
+                       bool shaded = false;
+                       if (t.asInput() == "\\begin") {
+                               p.skip_spaces(true);
+                               if (p.getArg('{', '}') == "shaded")
+                                       shaded = true;
+                       }
+                       p.popPosition();
+                       if (shaded)
+                               parse_outer_box(p, os, FLAG_END, outer,
+                                               parent_context, name, "shaded");
+                       else
+                               parse_box(p, os, 0, FLAG_END, outer, parent_context,
+                                         "", "", name, "", "");
+                       p.skip_spaces();
+                       break;
                }
-               p.getArg('{', '}');
-               p.skip_spaces(true);
-               Token t = p.get_token();
-               bool shaded = false;
-               if (t.asInput() == "\\begin") {
-                       p.skip_spaces(true);
-                       if (p.getArg('{', '}') == "shaded")
-                               shaded = true;
+
+               if (name == "comment") {
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       begin_inset(os, "Note Comment\n");
+                       os << "status open\n";
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       p.skip_spaces();
+                       skip_braces(p); // eat {} that might by set by LyX behind comments
+                       preamble.registerAutomaticallyLoadedPackage("verbatim");
+                       break;
                }
-               p.popPosition();
-               if (shaded)
-                       parse_outer_box(p, os, FLAG_END, outer,
-                                       parent_context, name, "shaded");
-               else
-                       parse_box(p, os, 0, FLAG_END, outer, parent_context,
-                                 "", "", name, "", "");
-               p.skip_spaces();
-       }
 
-       else if (name == "comment") {
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Note Comment\n");
-               os << "status open\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               p.skip_spaces();
-               skip_braces(p); // eat {} that might by set by LyX behind comments
-               preamble.registerAutomaticallyLoadedPackage("verbatim");
-       }
+               if (unstarred_name == "verbatim") {
+                       // FIXME: this should go in the generic code that
+                       // handles environments defined in layout file that
+                       // have "PassThru 1". However, the code over there is
+                       // already too complicated for my taste.
+                       string const ascii_name =
+                               (name == "verbatim*") ? "Verbatim*" : "Verbatim";
+                       parent_context.new_paragraph(os);
+                       Context context(true, parent_context.textclass,
+                                       &parent_context.textclass[from_ascii(ascii_name)]);
+                       string s = p.verbatimEnvironment(name);
+                       output_ert(os, s, context);
+                       p.skip_spaces();
+                       break;
+               }
 
-       else if (unstarred_name == "verbatim") {
-               // FIXME: this should go in the generic code that
-               // handles environments defined in layout file that
-               // have "PassThru 1". However, the code over there is
-               // already too complicated for my taste.
-               string const ascii_name =
-                       (name == "verbatim*") ? "Verbatim*" : "Verbatim";
-               parent_context.new_paragraph(os);
-               Context context(true, parent_context.textclass,
-                               &parent_context.textclass[from_ascii(ascii_name)]);
-               string s = p.verbatimEnvironment(name);
-               output_ert(os, s, context);
-               p.skip_spaces();
-       }
+               if (name == "IPA") {
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       begin_inset(os, "IPA\n");
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       p.skip_spaces();
+                       preamble.registerAutomaticallyLoadedPackage("tipa");
+                       preamble.registerAutomaticallyLoadedPackage("tipx");
+                       break;
+               }
 
-       else if (name == "IPA") {
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "IPA\n");
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               p.skip_spaces();
-               preamble.registerAutomaticallyLoadedPackage("tipa");
-               preamble.registerAutomaticallyLoadedPackage("tipx");
-       }
+               if (name == parent_context.textclass.titlename()
+                   && parent_context.textclass.titletype() == TITLE_ENVIRONMENT) {
+                       parse_text(p, os, FLAG_END, outer, parent_context);
+                       // Just in case the environment is empty
+                       parent_context.extra_stuff.erase();
+                       // We must begin a new paragraph
+                       parent_context.new_paragraph(os);
+                       p.skip_spaces();
+                       break;
+               }
 
-       else if (name == "CJK") {
-               // the scheme is \begin{CJK}{encoding}{mapping}text\end{CJK}
-               // It is impossible to decide if a CJK environment was in its own paragraph or within
-               // a line. We therefore always assume a paragraph since the latter is a rare case.
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_end_layout(os);
-               // store the encoding to be able to reset it
-               string const encoding_old = p.getEncoding();
-               string const encoding = p.getArg('{', '}');
-               // FIXME: For some reason JIS does not work. Although the text
-               // in tests/CJK.tex is identical with the SJIS version if you
-               // convert both snippets using the recode command line utility,
-               // the resulting .lyx file contains some extra characters if
-               // you set buggy_encoding to false for JIS.
-               bool const buggy_encoding = encoding == "JIS";
-               if (!buggy_encoding)
-                       p.setEncoding(encoding, Encoding::CJK);
-               else {
-                       // FIXME: This will read garbage, since the data is not encoded in utf8.
-                       p.setEncoding("UTF-8");
+               if (name == "CJK") {
+                       // the scheme is \begin{CJK}{encoding}{mapping}text\end{CJK}
+                       // It is impossible to decide if a CJK environment was in its own paragraph or within
+                       // a line. We therefore always assume a paragraph since the latter is a rare case.
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_end_layout(os);
+                       // store the encoding to be able to reset it
+                       string const encoding_old = p.getEncoding();
+                       string const encoding = p.getArg('{', '}');
+                       // FIXME: For some reason JIS does not work. Although the text
+                       // in tests/CJK.tex is identical with the SJIS version if you
+                       // convert both snippets using the recode command line utility,
+                       // the resulting .lyx file contains some extra characters if
+                       // you set buggy_encoding to false for JIS.
+                       bool const buggy_encoding = encoding == "JIS";
+                       if (!buggy_encoding)
+                               p.setEncoding(encoding, Encoding::CJK);
+                       else {
+                               // FIXME: This will read garbage, since the data is not encoded in utf8.
+                               p.setEncoding("UTF-8");
+                       }
+                       // LyX only supports the same mapping for all CJK
+                       // environments, so we might need to output everything as ERT
+                       string const mapping = trim(p.getArg('{', '}'));
+                       char const * const * const where =
+                               is_known(encoding, supported_CJK_encodings);
+                       if (!buggy_encoding && !preamble.fontCJKSet())
+                               preamble.fontCJK(mapping);
+                       bool knownMapping = mapping == preamble.fontCJK();
+                       if (buggy_encoding || !knownMapping || !where) {
+                               parent_context.check_layout(os);
+                               output_ert_inset(os, "\\begin{" + name + "}{" + encoding + "}{" + mapping + "}",
+                                              parent_context);
+                               // we must parse the content as verbatim because e.g. JIS can contain
+                               // normally invalid characters
+                               // FIXME: This works only for the most simple cases.
+                               //        Since TeX control characters are not parsed,
+                               //        things like comments are completely wrong.
+                               string const s = p.plainEnvironment("CJK");
+                               for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+                                       string snip;
+                                       snip += *it;
+                                       if (snip == "\\" || is_known(snip, known_escaped_chars))
+                                               output_ert_inset(os, snip, parent_context);
+                                       else if (*it == '\n' && it + 1 != et && s.begin() + 1 != it)
+                                               os << "\n ";
+                                       else
+                                               os << *it;
+                               }
+                               output_ert_inset(os, "\\end{" + name + "}",
+                                              parent_context);
+                       } else {
+                               string const lang =
+                                       supported_CJK_languages[where - supported_CJK_encodings];
+                               // store the language because we must reset it at the end
+                               string const lang_old = parent_context.font.language;
+                               parent_context.font.language = lang;
+                               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                               parent_context.font.language = lang_old;
+                               parent_context.new_paragraph(os);
+                       }
+                       p.setEncoding(encoding_old);
+                       p.skip_spaces();
+                       break;
                }
-               // LyX only supports the same mapping for all CJK
-               // environments, so we might need to output everything as ERT
-               string const mapping = trim(p.getArg('{', '}'));
-               char const * const * const where =
-                       is_known(encoding, supported_CJK_encodings);
-               if (!buggy_encoding && !preamble.fontCJKSet())
-                       preamble.fontCJK(mapping);
-               bool knownMapping = mapping == preamble.fontCJK();
-               if (buggy_encoding || !knownMapping || !where) {
+
+               if (name == "lyxgreyedout") {
+                       eat_whitespace(p, os, parent_context, false);
                        parent_context.check_layout(os);
-                       output_ert_inset(os, "\\begin{" + name + "}{" + encoding + "}{" + mapping + "}",
-                                      parent_context);
-                       // we must parse the content as verbatim because e.g. JIS can contain
-                       // normally invalid characters
-                       // FIXME: This works only for the most simple cases.
-                       //        Since TeX control characters are not parsed,
-                       //        things like comments are completely wrong.
-                       string const s = p.plainEnvironment("CJK");
-                       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
-                               string snip;
-                               snip += *it;
-                               if (snip == "\\" || is_known(snip, known_escaped_chars))
-                                       output_ert_inset(os, snip, parent_context);
-                               else if (*it == '\n' && it + 1 != et && s.begin() + 1 != it)
-                                       os << "\n ";
-                               else
-                                       os << *it;
-                       }
-                       output_ert_inset(os, "\\end{" + name + "}",
-                                      parent_context);
-               } else {
-                       string const lang =
-                               supported_CJK_languages[where - supported_CJK_encodings];
-                       // store the language because we must reset it at the end
-                       string const lang_old = parent_context.font.language;
-                       parent_context.font.language = lang;
+                       begin_inset(os, "Note Greyedout\n");
+                       os << "status open\n";
                        parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-                       parent_context.font.language = lang_old;
-                       parent_context.new_paragraph(os);
+                       end_inset(os);
+                       p.skip_spaces();
+                       if (!preamble.notefontcolor().empty())
+                               preamble.registerAutomaticallyLoadedPackage("color");
+                       break;
                }
-               p.setEncoding(encoding_old);
-               p.skip_spaces();
-       }
 
-       else if (name == "lyxgreyedout") {
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Note Greyedout\n");
-               os << "status open\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               p.skip_spaces();
-               if (!preamble.notefontcolor().empty())
-                       preamble.registerAutomaticallyLoadedPackage("color");
-       }
-
-       else if (name == "btSect") {
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_command_inset(os, "bibtex", "bibtex");
-               string bibstyle = "plain";
-               if (p.hasOpt()) {
-                       bibstyle = p.getArg('[', ']');
-                       p.skip_spaces(true);
+               if (name == "btSect") {
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       begin_command_inset(os, "bibtex", "bibtex");
+                       string bibstyle = "plain";
+                       if (p.hasOpt()) {
+                               bibstyle = p.getArg('[', ']');
+                               p.skip_spaces(true);
+                       }
+                       string const bibfile = p.getArg('{', '}');
+                       eat_whitespace(p, os, parent_context, false);
+                       Token t = p.get_token();
+                       if (t.asInput() == "\\btPrintCited") {
+                               p.skip_spaces(true);
+                               os << "btprint " << '"' << "btPrintCited" << '"' << "\n";
+                       }
+                       if (t.asInput() == "\\btPrintNotCited") {
+                               p.skip_spaces(true);
+                               os << "btprint " << '"' << "btPrintNotCited" << '"' << "\n";
+                       }
+                       if (t.asInput() == "\\btPrintAll") {
+                               p.skip_spaces(true);
+                               os << "btprint " << '"' << "btPrintAll" << '"' << "\n";
+                       }
+                       os << "bibfiles " << '"' << bibfile << "\"\n"
+                          << "options " << '"' << bibstyle << "\"\n";
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       end_inset(os);
+                       p.skip_spaces();
+                       break;
                }
-               string const bibfile = p.getArg('{', '}');
-               eat_whitespace(p, os, parent_context, false);
-               Token t = p.get_token();
-               if (t.asInput() == "\\btPrintCited") {
-                       p.skip_spaces(true);
-                       os << "btprint " << '"' << "btPrintCited" << '"' << "\n";
+
+               if (name == "btUnit") {
+                       string const nt = p.next_next_token().cs();
+                       // Do not attempt to overwrite a former diverging multibib.
+                       // Those are output as ERT instead.
+                       if ((nt == "part" || nt == "chapter"
+                            || nt == "section" || nt == "subsection")
+                          && (preamble.multibib().empty() || preamble.multibib() == nt)) {
+                               parse_text(p, os, FLAG_END, outer, parent_context);
+                               preamble.multibib(nt);
+                       } else
+                               parse_unknown_environment(p, name, os, FLAG_END, outer,
+                                                         parent_context);
+                       break;
                }
-               if (t.asInput() == "\\btPrintNotCited") {
-                       p.skip_spaces(true);
-                       os << "btprint " << '"' << "btPrintNotCited" << '"' << "\n";
+
+               // This is only attempted at turn environments that consist only
+               // of a tabular (this is how tables in LyX, modulo longtables, are rotated).
+               // Thus we will fall through in other cases.
+               if (name == "turn") {
+                       // We check if the next thing is a tabular[*|x]
+                       p.pushPosition();
+                       p.getArg('{', '}');// eat turn argument
+                       bool found_end = false;
+                       bool only_table = false;
+                       bool end_table = false;
+                       p.get_token();
+                       p.get_token();
+                       string envname = p.getArg('{', '}');
+                       if (rtrim(envname, "*") == "tabular" || envname == "tabularx") {
+                               // Now we check if the table is the only content
+                               // of the turn environment
+                               string const tenv = envname;
+                               while (!found_end && !end_table && p.good()) {
+                                       envname = p.next_token().cat() == catBegin
+                                                       ? p.getArg('{', '}') : string();
+                                       Token const & t = p.get_token();
+                                       p.skip_spaces();
+                                       end_table = t.asInput() != "\\end"
+                                                       && envname == tenv;
+                                       found_end = t.asInput() == "\\end"
+                                                       && envname == "turn";
+                               }
+                               if (end_table) {
+                                       p.get_token();
+                                       envname = p.getArg('{', '}');
+                                       only_table = p.next_next_token().asInput() == "\\end"
+                                                       && envname == "turn";
+                               }
+                               if (only_table) {
+                                       p.popPosition();
+                                       string const angle = p.getArg('{', '}');
+                                       p.skip_spaces();
+                                       int const save_tablerotation = parent_context.tablerotation;
+                                       parent_context.tablerotation = convert<int>(angle);
+                                       parse_text(p, os, FLAG_END, outer, parent_context);
+                                       parent_context.tablerotation = save_tablerotation;
+                                       p.skip_spaces();
+                                       break;
+                               }
+                               // fall through
+                       }
+                       // fall through
+                       p.popPosition();
                }
-               if (t.asInput() == "\\btPrintAll") {
-                       p.skip_spaces(true);
-                       os << "btprint " << '"' << "btPrintAll" << '"' << "\n";
+
+               // This is only attempted at landscape environments that consist only
+               // of a longtable (this is how longtables in LyX are rotated by 90 degs).
+               // Other landscape environment is handled via the landscape module, thus
+               // we will fall through in that case.
+               if (name == "landscape") {
+                       // We check if the next thing is a longtable
+                       p.pushPosition();
+                       bool found_end = false;
+                       bool only_longtable = false;
+                       bool end_longtable = false;
+                       p.get_token();
+                       p.get_token();
+                       string envname = p.getArg('{', '}');
+                       if (envname == "longtable" || envname == "xltabular") {
+                               // Now we check if the longtable is the only content
+                               // of the landscape environment
+                               string const ltenv = envname;
+                               while (!found_end && !end_longtable && p.good()) {
+                                       envname = p.next_token().cat() == catBegin
+                                                       ? p.getArg('{', '}') : string();
+                                       Token const & t = p.get_token();
+                                       p.skip_spaces();
+                                       end_longtable = t.asInput() != "\\end"
+                                                       && envname == ltenv;
+                                       found_end = t.asInput() == "\\end"
+                                                       && envname == "landscape";
+                               }
+                               if (end_longtable) {
+                                       p.get_token();
+                                       envname = p.getArg('{', '}');
+                                       only_longtable = p.next_next_token().asInput() == "\\end"
+                                                       && envname == "landscape";
+                               }
+                               if (only_longtable) {
+                                       p.popPosition();
+                                       p.skip_spaces();
+                                       int const save_tablerotation = parent_context.tablerotation;
+                                       parent_context.tablerotation = 90;
+                                       parse_text(p, os, FLAG_END, outer, parent_context);
+                                       parent_context.tablerotation = save_tablerotation;
+                                       p.skip_spaces();
+                                       break;
+                               }
+                               // fall through
+                       }
+                       // fall through
+                       p.popPosition();
                }
-               os << "bibfiles " << '"' << bibfile << "\"\n"
-                  << "options " << '"' << bibstyle << "\"\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
-               p.skip_spaces();
-       }
-
-       else if (name == "framed" || name == "shaded") {
-               eat_whitespace(p, os, parent_context, false);
-               parse_outer_box(p, os, FLAG_END, outer, parent_context, name, "");
-               p.skip_spaces();
-               preamble.registerAutomaticallyLoadedPackage("framed");
-       }
 
-       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);
+               if (name == "framed" || name == "shaded") {
+                       eat_whitespace(p, os, parent_context, false);
+                       parse_outer_box(p, os, FLAG_END, outer, parent_context, name, "");
+                       p.skip_spaces();
+                       preamble.registerAutomaticallyLoadedPackage("framed");
+                       break;
                }
-               parent_context.check_layout(os);
-               if (minted_env && minted_float_has_caption) {
+
+               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);
-                       os << content << "\n";
-                       if (!caption.empty())
+                       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";
-                       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) {
+                               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";
                        }
-                       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;
+                       break;
                }
-               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()) {
+               if (name == "lstlisting" || name == "minted") {
+                       bool use_minted = name == "minted";
+                       // with listings, we do not eat newlines here since
+                       // \begin{lstlistings}
+                       // [foo]
+                       // and
+                       // // \begin{lstlistings}%
+                       //
+                       // [foo]
+                       // reads [foo] as content, whereas
+                       // // \begin{lstlistings}%
+                       // [foo]
+                       // or
+                       // \begin{lstlistings}[foo,
+                       // bar]
+                       // reads [foo...] as argument.
+                       eat_whitespace(p, os, parent_context, false, use_minted);
+                       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();
-                               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.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;
+                                               eat_whitespace(p, os, parent_context, true);
+                                       }
                                }
+                               p.popPosition();
                        }
-                       p.popPosition();
+                       parse_listings(p, os, parent_context, false, use_minted);
+                       p.skip_spaces();
+                       break;
                }
-               parse_listings(p, os, parent_context, false, use_minted);
-               p.skip_spaces();
-       }
 
-       else if (!parent_context.new_layout_allowed)
-               parse_unknown_environment(p, name, os, FLAG_END, outer,
-                                         parent_context);
-
-       // Alignment and spacing settings
-       // FIXME (bug xxxx): These settings can span multiple paragraphs and
-       //                                       therefore are totally broken!
-       // Note that \centering, \raggedright, and \raggedleft cannot be handled, as
-       // they are commands not environments. They are furthermore switches that
-       // can be ended by another switches, but also by commands like \footnote or
-       // \parbox. So the only safe way is to leave them untouched.
-       // However, we support the pseudo-environments
-       // \begin{centering} ... \end{centering}
-       // \begin{raggedright} ... \end{raggedright}
-       // \begin{raggedleft} ... \end{raggedleft}
-       // since they are used by LyX in floats (for spacing reasons)
-       else if (name == "center" || name == "centering" ||
-                name == "flushleft" || name == "raggedright" ||
-                name == "flushright" || name == "raggedleft" ||
-                name == "singlespace" || name == "onehalfspace" ||
-                name == "doublespace" || name == "spacing") {
-               eat_whitespace(p, os, parent_context, false);
-               // We must begin a new paragraph if not already done
-               if (! parent_context.atParagraphStart()) {
-                       parent_context.check_end_layout(os);
-                       parent_context.new_paragraph(os);
+               if (!parent_context.new_layout_allowed) {
+                       parse_unknown_environment(p, name, os, FLAG_END, outer,
+                                                 parent_context);
+                       break;
                }
-               if (name == "flushleft" || name == "raggedright")
-                       parent_context.add_extra_stuff("\\align left\n");
-               else if (name == "flushright" || name == "raggedleft")
-                       parent_context.add_extra_stuff("\\align right\n");
-               else if (name == "center" || name == "centering")
-                       parent_context.add_extra_stuff("\\align center\n");
-               else if (name == "singlespace")
-                       parent_context.add_extra_stuff("\\paragraph_spacing single\n");
-               else if (name == "onehalfspace") {
-                       parent_context.add_extra_stuff("\\paragraph_spacing onehalf\n");
-                       preamble.registerAutomaticallyLoadedPackage("setspace");
-               } else if (name == "doublespace") {
-                       parent_context.add_extra_stuff("\\paragraph_spacing double\n");
-                       preamble.registerAutomaticallyLoadedPackage("setspace");
-               } else if (name == "spacing") {
-                       parent_context.add_extra_stuff("\\paragraph_spacing other " + p.verbatim_item() + "\n");
-                       preamble.registerAutomaticallyLoadedPackage("setspace");
-               }
-               parse_text(p, os, FLAG_END, outer, parent_context);
-               // Just in case the environment is empty
-               parent_context.extra_stuff.erase();
-               // We must begin a new paragraph to reset the alignment
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-       }
 
-       // The single '=' is meant here.
-       else if ((newlayout = findLayout(parent_context.textclass, name, false))) {
-               eat_whitespace(p, os, parent_context, false);
-               Context context(true, parent_context.textclass, newlayout,
-                               parent_context.layout, parent_context.font);
-               if (parent_context.deeper_paragraph) {
-                       // We are beginning a nested environment after a
-                       // deeper paragraph inside the outer list environment.
-                       // Therefore we don't need to output a "begin deeper".
-                       context.need_end_deeper = true;
-               }
-               parent_context.check_end_layout(os);
-               if (last_env == name) {
-                       // we need to output a separator since LyX would export
-                       // the two environments as one otherwise (bug 5716)
-                       TeX2LyXDocClass const & textclass(parent_context.textclass);
-                       Context newcontext(true, textclass,
-                                       &(textclass.defaultLayout()));
-                       newcontext.check_layout(os);
-                       begin_inset(os, "Separator plain\n");
-                       end_inset(os);
-                       newcontext.check_end_layout(os);
-               }
-               switch (context.layout->latextype) {
-               case  LATEX_LIST_ENVIRONMENT:
-                       context.add_par_extra_stuff("\\labelwidthstring "
-                                                   + p.verbatim_item() + '\n');
+               // Alignment and spacing settings
+               // FIXME (bug xxxx): These settings can span multiple paragraphs and
+               //                                       therefore are totally broken!
+               // Note that \centering, \raggedright, and \raggedleft cannot be handled, as
+               // they are commands not environments. They are furthermore switches that
+               // can be ended by another switches, but also by commands like \footnote or
+               // \parbox. So the only safe way is to leave them untouched.
+               // However, we support the pseudo-environments
+               // \begin{centering} ... \end{centering}
+               // \begin{raggedright} ... \end{raggedright}
+               // \begin{raggedleft} ... \end{raggedleft}
+               // since they are used by LyX in floats (for spacing reasons)
+               if (name == "center" || name == "centering"
+                   || name == "flushleft" || name == "raggedright"
+                   || name == "flushright" || name == "raggedleft"
+                   || name == "singlespace" || name == "onehalfspace"
+                   || name == "doublespace" || name == "spacing") {
+                       eat_whitespace(p, os, parent_context, false);
+                       // We must begin a new paragraph if not already done
+                       if (! parent_context.atParagraphStart()) {
+                               parent_context.check_end_layout(os);
+                               parent_context.new_paragraph(os);
+                       }
+                       if (name == "flushleft" || name == "raggedright")
+                               parent_context.add_extra_stuff("\\align left\n");
+                       else if (name == "flushright" || name == "raggedleft")
+                               parent_context.add_extra_stuff("\\align right\n");
+                       else if (name == "center" || name == "centering")
+                               parent_context.add_extra_stuff("\\align center\n");
+                       else if (name == "singlespace")
+                               parent_context.add_extra_stuff("\\paragraph_spacing single\n");
+                       else if (name == "onehalfspace") {
+                               parent_context.add_extra_stuff("\\paragraph_spacing onehalf\n");
+                               preamble.registerAutomaticallyLoadedPackage("setspace");
+                       } else if (name == "doublespace") {
+                               parent_context.add_extra_stuff("\\paragraph_spacing double\n");
+                               preamble.registerAutomaticallyLoadedPackage("setspace");
+                       } else if (name == "spacing") {
+                               parent_context.add_extra_stuff("\\paragraph_spacing other " + p.verbatim_item() + "\n");
+                               preamble.registerAutomaticallyLoadedPackage("setspace");
+                       }
+                       parse_text(p, os, FLAG_END, outer, parent_context);
+                       // Just in case the environment is empty
+                       parent_context.extra_stuff.erase();
+                       // We must begin a new paragraph to reset the alignment
+                       parent_context.new_paragraph(os);
                        p.skip_spaces();
                        break;
-               case  LATEX_BIB_ENVIRONMENT:
-                       p.verbatim_item(); // swallow next arg
+               }
+
+               // The single '=' is meant here.
+               if ((newlayout = findLayout(parent_context.textclass, name, false))) {
+                       eat_whitespace(p, os, parent_context, false);
+                       Context context(true, parent_context.textclass, newlayout,
+                                       parent_context.layout, parent_context.font);
+                       if (parent_context.deeper_paragraph) {
+                               // We are beginning a nested environment after a
+                               // deeper paragraph inside the outer list environment.
+                               // Therefore we don't need to output a "begin deeper".
+                               context.need_end_deeper = true;
+                       }
+                       parent_context.check_end_layout(os);
+                       if (last_env == name) {
+                               // we need to output a separator since LyX would export
+                               // the two environments as one otherwise (bug 5716)
+                               TeX2LyXDocClass const & textclass(parent_context.textclass);
+                               Context newcontext(true, textclass,
+                                               &(textclass.defaultLayout()));
+                               newcontext.check_layout(os);
+                               begin_inset(os, "Separator plain\n");
+                               end_inset(os);
+                               newcontext.check_end_layout(os);
+                       }
+                       switch (context.layout->latextype) {
+                       case  LATEX_LIST_ENVIRONMENT:
+                               context.in_list_preamble =
+                                       !context.layout->listpreamble().empty()
+                                       && p.hasListPreamble(context.layout->itemcommand());
+                               context.add_par_extra_stuff("\\labelwidthstring "
+                                                           + p.verbatim_item() + '\n');
+                               p.skip_spaces();
+                               break;
+                       case  LATEX_BIB_ENVIRONMENT:
+                               p.verbatim_item(); // swallow next arg
+                               p.skip_spaces();
+                               break;
+                       default:
+                               break;
+                       }
+                       context.check_deeper(os);
+                       if (newlayout->keepempty) {
+                               // We need to start a new paragraph
+                               // even if it is empty.
+                               context.new_paragraph(os);
+                               context.check_layout(os);
+                       }
+                       // handle known optional and required arguments
+                       if (context.layout->latextype == LATEX_ENVIRONMENT)
+                               output_arguments(os, p, outer, false, string(), context,
+                                                context.layout->latexargs());
+                       else if (context.layout->latextype == LATEX_ITEM_ENVIRONMENT) {
+                               context.in_list_preamble =
+                                       !context.layout->listpreamble().empty()
+                                       && p.hasListPreamble(context.layout->itemcommand());
+                               ostringstream oss;
+                               output_arguments(oss, p, outer, false, string(), context,
+                                                context.layout->latexargs());
+                               context.list_extra_stuff = oss.str();
+                       }
+                       if (context.in_list_preamble) {
+                               // Collect the stuff between \begin and first \item
+                               context.list_preamble =
+                                       parse_text_snippet(p, FLAG_END, outer, context);
+                               context.in_list_preamble = false;
+                       }
+                       parse_text(p, os, FLAG_END, outer, context);
+                       if (context.layout->latextype == LATEX_ENVIRONMENT)
+                               output_arguments(os, p, outer, false, "post", context,
+                                                context.layout->postcommandargs());
+                       context.check_end_layout(os);
+                       if (parent_context.deeper_paragraph) {
+                               // We must suppress the "end deeper" because we
+                               // suppressed the "begin deeper" above.
+                               context.need_end_deeper = false;
+                       }
+                       context.check_end_deeper(os);
+                       parent_context.new_paragraph(os);
                        p.skip_spaces();
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(newlayout->intitle);
+                       set<string> const & req = newlayout->required();
+                       set<string>::const_iterator it = req.begin();
+                       set<string>::const_iterator en = req.end();
+                       for (; it != en; ++it)
+                               preamble.registerAutomaticallyLoadedPackage(*it);
                        break;
-               default:
-                       break;
-               }
-               context.check_deeper(os);
-               // handle known optional and required arguments
-               // Unfortunately LyX can't handle arguments of list arguments (bug 7468):
-               // It is impossible to place anything after the environment name,
-               // but before the first \\item.
-               if (context.layout->latextype == LATEX_ENVIRONMENT)
-                       output_arguments(os, p, outer, false, false, context,
-                                        context.layout->latexargs());
-               parse_text(p, os, FLAG_END, outer, context);
-               if (context.layout->latextype == LATEX_ENVIRONMENT)
-                       output_arguments(os, p, outer, false, true, context,
-                                        context.layout->postcommandargs());
-               context.check_end_layout(os);
-               if (parent_context.deeper_paragraph) {
-                       // We must suppress the "end deeper" because we
-                       // suppressed the "begin deeper" above.
-                       context.need_end_deeper = false;
                }
-               context.check_end_deeper(os);
-               parent_context.new_paragraph(os);
-               p.skip_spaces();
-               if (!preamble.titleLayoutFound())
-                       preamble.titleLayoutFound(newlayout->intitle);
-               set<string> const & req = newlayout->requires();
-               set<string>::const_iterator it = req.begin();
-               set<string>::const_iterator en = req.end();
-               for (; it != en; ++it)
-                       preamble.registerAutomaticallyLoadedPackage(*it);
-       }
 
-       // The single '=' is meant here.
-       else if ((newinsetlayout = findInsetLayout(parent_context.textclass, name, false))) {
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Flex ");
-               docstring flex_name = newinsetlayout->name();
-               // FIXME: what do we do if the prefix is not Flex: ?
-               if (prefixIs(flex_name, from_ascii("Flex:")))
-                       flex_name.erase(0, 5);
-               os << to_utf8(flex_name) << '\n'
-                  << "status collapsed\n";
-               if (newinsetlayout->isPassThru()) {
-                       string const arg = p.verbatimEnvironment(name);
-                       Context context(true, parent_context.textclass,
-                                       &parent_context.textclass.plainLayout(),
-                                       parent_context.layout);
-                       output_ert(os, arg, parent_context);
-               } else
-                       parse_text_in_inset(p, os, FLAG_END, false, parent_context, newinsetlayout);
-               end_inset(os);
-       }
+               // The single '=' is meant here.
+               if ((newinsetlayout = findInsetLayout(parent_context.textclass, name, false))) {
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_layout(os);
+                       begin_inset(os, "Flex ");
+                       docstring flex_name = newinsetlayout->name();
+                       // FIXME: what do we do if the prefix is not Flex: ?
+                       if (prefixIs(flex_name, from_ascii("Flex:")))
+                               flex_name.erase(0, 5);
+                       os << to_utf8(flex_name) << '\n'
+                          << "status collapsed\n";
+                       if (newinsetlayout->isPassThru()) {
+                               string const arg = p.verbatimEnvironment(name);
+                               Context context(true, parent_context.textclass,
+                                               &parent_context.textclass.plainLayout(),
+                                               parent_context.layout);
+                               output_ert(os, arg, parent_context);
+                       } else
+                               parse_text_in_inset(p, os, FLAG_END, false, parent_context, newinsetlayout);
+                       end_inset(os);
+                       break;
+               }
 
-       else if (name == "appendix") {
-               // This is no good latex style, but it works and is used in some documents...
-               eat_whitespace(p, os, parent_context, false);
-               parent_context.check_end_layout(os);
-               Context context(true, parent_context.textclass, parent_context.layout,
-                               parent_context.layout, parent_context.font);
-               context.check_layout(os);
-               os << "\\start_of_appendix\n";
-               parse_text(p, os, FLAG_END, outer, context);
-               context.check_end_layout(os);
-               p.skip_spaces();
-       }
+               if (name == "appendix") {
+                       // This is no good latex style, but it works and is used in some documents...
+                       eat_whitespace(p, os, parent_context, false);
+                       parent_context.check_end_layout(os);
+                       Context context(true, parent_context.textclass, parent_context.layout,
+                                       parent_context.layout, parent_context.font);
+                       context.check_layout(os);
+                       os << "\\start_of_appendix\n";
+                       parse_text(p, os, FLAG_END, outer, context);
+                       context.check_end_layout(os);
+                       p.skip_spaces();
+                       break;
+               }
 
-       else if (known_environments.find(name) != known_environments.end()) {
-               vector<ArgumentType> arguments = known_environments[name];
-               // The last "argument" denotes wether we may translate the
-               // environment contents to LyX
-               // The default required if no argument is given makes us
-               // compatible with the reLyXre environment.
-               ArgumentType contents = arguments.empty() ?
-                       required :
-                       arguments.back();
-               if (!arguments.empty())
-                       arguments.pop_back();
-               // See comment in parse_unknown_environment()
-               bool const specialfont =
-                       (parent_context.font != parent_context.normalfont);
-               bool const new_layout_allowed =
-                       parent_context.new_layout_allowed;
-               if (specialfont)
-                       parent_context.new_layout_allowed = false;
-               parse_arguments("\\begin{" + name + "}", arguments, p, os,
-                               outer, parent_context);
-               if (contents == verbatim)
-                       output_ert_inset(os, p.ertEnvironment(name),
-                                  parent_context);
-               else
-                       parse_text_snippet(p, os, FLAG_END, outer,
+               if (known_environments.find(name) != known_environments.end()) {
+                       vector<ArgumentType> arguments = known_environments[name];
+                       // The last "argument" denotes wether we may translate the
+                       // environment contents to LyX
+                       // The default required if no argument is given makes us
+                       // compatible with the reLyXre environment.
+                       ArgumentType contents = arguments.empty() ?
+                               required :
+                               arguments.back();
+                       if (!arguments.empty())
+                               arguments.pop_back();
+                       // See comment in parse_unknown_environment()
+                       bool const specialfont =
+                               (parent_context.font != parent_context.normalfont);
+                       bool const new_layout_allowed =
+                               parent_context.new_layout_allowed;
+                       if (specialfont)
+                               parent_context.new_layout_allowed = false;
+                       parse_arguments("\\begin{" + name + "}", arguments, p, os,
+                                       outer, parent_context);
+                       if (contents == verbatim)
+                               output_ert_inset(os, p.ertEnvironment(name),
                                           parent_context);
-               output_ert_inset(os, "\\end{" + name + "}", parent_context);
-               if (specialfont)
-                       parent_context.new_layout_allowed = new_layout_allowed;
-       }
+                       else
+                               parse_text_snippet(p, os, FLAG_END, outer,
+                                                  parent_context);
+                       output_ert_inset(os, "\\end{" + name + "}", parent_context);
+                       if (specialfont)
+                               parent_context.new_layout_allowed = new_layout_allowed;
+                       break;
+               }
 
-       else
-               parse_unknown_environment(p, name, os, FLAG_END, outer,
-                                         parent_context);
+               parse_unknown_environment(p, name, os, FLAG_END, outer, parent_context);
+               break;
+       }// end of loop
 
        last_env = name;
        active_environments.pop_back();
@@ -2091,7 +2442,8 @@ void parse_environment(Parser & p, ostream & os, bool outer,
 
 
 /// parses a comment and outputs it to \p os.
-void parse_comment(Parser & p, ostream & os, Token const & t, Context & context)
+void parse_comment(Parser & p, ostream & os, Token const & t, Context & context,
+                  bool skipNewlines = false)
 {
        LASSERT(t.cat() == catComment, return);
        if (!t.cs().empty()) {
@@ -2109,7 +2461,7 @@ void parse_comment(Parser & p, ostream & os, Token const & t, Context & context)
                                output_ert_inset(os, "\n", context);
                        eat_whitespace(p, os, context, true);
                }
-       } else {
+       } else if (!skipNewlines) {
                // "%\n" combination
                p.skip_spaces();
        }
@@ -2120,17 +2472,18 @@ void parse_comment(Parser & p, ostream & os, Token const & t, Context & context)
  * Reads spaces and comments until the first non-space, non-comment token.
  * New paragraphs (double newlines or \\par) are handled like simple spaces
  * if \p eatParagraph is true.
+ * If \p eatNewline is false, newlines won't be treated as whitespace.
  * Spaces are skipped, but comments are written to \p os.
  */
 void eat_whitespace(Parser & p, ostream & os, Context & context,
-                   bool eatParagraph)
+                   bool eatParagraph, bool eatNewline)
 {
        while (p.good()) {
                Token const & t = p.get_token();
                if (t.cat() == catComment)
-                       parse_comment(p, os, t, context);
-               else if ((! eatParagraph && p.isParagraph()) ||
-                        (t.cat() != catSpace && t.cat() != catNewline)) {
+                       parse_comment(p, os, t, context, !eatNewline);
+               else if ((!eatParagraph && p.isParagraph()) ||
+                        (t.cat() != catSpace && (t.cat() != catNewline || !eatNewline))) {
                        p.putback();
                        return;
                }
@@ -2510,7 +2863,7 @@ void fix_child_filename(string & name)
 
 
 void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context & context)
+               Context & context, string const rdelim)
 {
        Layout const * newlayout = 0;
        InsetLayout const * newinsetlayout = 0;
@@ -2566,6 +2919,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                debugToken(cerr, t, flags);
 #endif
 
+               if (context.in_list_preamble
+                   && p.next_token().cs() == context.layout->itemcommand()) {
+                       // We are parsing a list preamble. End before first \item.
+                       flags |= FLAG_LEAVE;
+                       context.in_list_preamble = false;
+               }
+
                if (flags & FLAG_ITEM) {
                        if (t.cat() == catSpace)
                                continue;
@@ -2587,6 +2947,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        return;
                if (t.cat() == catEnd && (flags & FLAG_BRACE_LAST))
                        return;
+               string tok = t.asInput();
+               // we only support delimiters with max 2 chars for now.
+               if (rdelim.size() > 1)
+                       tok += p.next_token().asInput();
+               if (t.cat() != catEscape && !rdelim.empty()
+                   && tok == rdelim && (flags & FLAG_RDELIM)) {
+                       if (rdelim.size() > 1)
+                               p.get_token(); // eat rdelim
+                       return;
+               }
 
                // If there is anything between \end{env} and \begin{env} we
                // don't need to output a separator.
@@ -2634,14 +3004,17 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               // Basic support for english quotes. This should be
-               // extended to other quotes, but is not so easy (a
-               // left english quote is the same as a right german
-               // quote...)
+               // Basic support for quotes. We try to disambiguate
+               // quotes from the context (e.g., a left english quote is
+               // the same as a right german quote...).
+               // Try to make a smart guess about the side
+               Token const prev = p.prev_token();
+               bool const opening = (prev.cat() != catSpace && prev.character() != 0
+                               && prev.character() != '\n' && prev.character() != '~');
                if (t.asInput() == "`" && p.next_token().asInput() == "`") {
                        context.check_layout(os);
                        begin_inset(os, "Quotes ");
-                       os << "eld";
+                       os << guessQuoteStyle("eld", opening);
                        end_inset(os);
                        p.get_token();
                        skip_braces(p);
@@ -2650,7 +3023,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                if (t.asInput() == "'" && p.next_token().asInput() == "'") {
                        context.check_layout(os);
                        begin_inset(os, "Quotes ");
-                       os << "erd";
+                       os << guessQuoteStyle("erd", opening);
                        end_inset(os);
                        p.get_token();
                        skip_braces(p);
@@ -2660,7 +3033,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                if (t.asInput() == ">" && p.next_token().asInput() == ">") {
                        context.check_layout(os);
                        begin_inset(os, "Quotes ");
-                       os << "ald";
+                       os << guessQuoteStyle("ald", opening);
                        end_inset(os);
                        p.get_token();
                        skip_braces(p);
@@ -2681,9 +3054,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (!has_chunk) {
                                context.check_layout(os);
                                begin_inset(os, "Quotes ");
-                               //FIXME: this is a right danish quote;
-                               // why not a left french quote?
-                               os << "ard";
+                               os << guessQuoteStyle("ard", opening);
                                end_inset(os);
                                p.get_token();
                                skip_braces(p);
@@ -2809,8 +3180,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                   is_known(next.cs(), known_quotes) &&
                                   end.cat() == catEnd) {
                                // Something like {\textquoteright} (e.g.
-                               // from writer2latex). LyX writes
-                               // \textquoteright{}, so we may skip the
+                               // from writer2latex). We may skip the
                                // braces here for better readability.
                                parse_text_snippet(p, os, FLAG_BRACE_LAST,
                                                   outer, context);
@@ -2975,10 +3345,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               if (t.cs() == "item") {
+               // "item" by default, but could be something else
+               if (t.cs() == context.layout->itemcommand()) {
                        string s;
-                       bool const optarg = p.hasOpt();
-                       if (optarg) {
+                       if (context.layout->labeltype == LABEL_MANUAL) {
                                // FIXME: This swallows comments, but we cannot use
                                //        eat_whitespace() since we must not output
                                //        anything before the item.
@@ -2992,26 +3362,29 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                // An item in an unknown list-like environment
                                // FIXME: Do this in check_layout()!
                                context.has_item = false;
-                               if (optarg)
-                                       output_ert_inset(os, "\\item", context);
-                               else
-                                       output_ert_inset(os, "\\item ", context);
-                       }
-                       if (optarg) {
-                               if (context.layout->labeltype != LABEL_MANUAL) {
-                                       // handle option of itemize item
-                                       begin_inset(os, "Argument item:1\n");
-                                       os << "status open\n";
-                                       os << "\n\\begin_layout Plain Layout\n";
-                                       Parser p2(s + ']');
-                                       os << parse_text_snippet(p2,
-                                               FLAG_BRACK_LAST, outer, context);
-                                       // we must not use context.check_end_layout(os)
-                                       // because that would close the outer itemize layout
-                                       os << "\n\\end_layout\n";
-                                       end_inset(os);
-                                       eat_whitespace(p, os, context, false);
-                               } else if (!s.empty()) {
+                               string item = "\\" + context.layout->itemcommand();
+                               if (!p.hasOpt())
+                                       item += " ";
+                               output_ert_inset(os, item, context);
+                       }
+                       if (context.layout->labeltype != LABEL_MANUAL)
+                               output_arguments(os, p, outer, false, "item", context,
+                                                context.layout->itemargs());
+                       if (!context.list_preamble.empty()) {
+                               // We have a list preamble. Output it here.
+                               begin_inset(os, "Argument listpreamble:1");
+                               os << "\nstatus collapsed\n\n"
+                                  << "\\begin_layout Plain Layout\n\n"
+                                  << rtrim(context.list_preamble)
+                                  << "\n\\end_layout";
+                               end_inset(os);
+                               context.list_preamble.clear();
+                       }
+                       if (!context.list_extra_stuff.empty()) {
+                               os << context.list_extra_stuff;
+                               context.list_extra_stuff.clear();
+                       }
+                       else if (!s.empty()) {
                                        // LyX adds braces around the argument,
                                        // so we need to remove them here.
                                        if (s.size() > 2 && s[0] == '{' &&
@@ -3033,7 +3406,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        os << ' ';
                                        eat_whitespace(p, os, context, false);
                                }
-                       }
                        continue;
                }
 
@@ -3161,7 +3533,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                                        if (!preamble.titleLayoutFound())
                                                preamble.titleLayoutFound(newlayout->intitle);
-                                       set<string> const & req = newlayout->requires();
+                                       set<string> const & req = newlayout->required();
                                        set<string>::const_iterator it = req.begin();
                                        set<string>::const_iterator en = req.end();
                                        for (; it != en; ++it)
@@ -3174,6 +3546,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);
+                       context.latexparam.clear();
+                       p.skip_spaces();
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(newlayout->intitle);
+                       set<string> const & req = newlayout->required();
+                       for (set<string>::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() == "*") &&
@@ -3185,7 +3576,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.skip_spaces();
                        if (!preamble.titleLayoutFound())
                                preamble.titleLayoutFound(newlayout->intitle);
-                       set<string> const & req = newlayout->requires();
+                       set<string> const & req = newlayout->required();
                        for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
                                preamble.registerAutomaticallyLoadedPackage(*it);
                        continue;
@@ -3199,7 +3590,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.skip_spaces();
                        if (!preamble.titleLayoutFound())
                                preamble.titleLayoutFound(newlayout->intitle);
-                       set<string> const & req = newlayout->requires();
+                       set<string> const & req = newlayout->required();
                        for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
                                preamble.registerAutomaticallyLoadedPackage(*it);
                        continue;
@@ -3342,9 +3733,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (opts.find("width") != opts.end())
                                os << "\twidth "
                                   << translate_len(opts["width"]) << '\n';
-                       if (opts.find("height") != opts.end())
+                       if (opts.find("totalheight") != opts.end())
                                os << "\theight "
-                                  << translate_len(opts["height"]) << '\n';
+                                  << translate_len(opts["totalheight"]) << '\n';
                        if (opts.find("scale") != opts.end()) {
                                istringstream iss(opts["scale"]);
                                double val;
@@ -3360,7 +3751,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                vector<string>::const_iterator s =
                                        find(keys.begin(), keys.end(), "width");
                                if (s == keys.end())
-                                       s = find(keys.begin(), keys.end(), "height");
+                                       s = find(keys.begin(), keys.end(), "totalheight");
                                if (s == keys.end())
                                        s = find(keys.begin(), keys.end(), "scale");
                                if (s != keys.end() && distance(s, a) > 0)
@@ -3421,8 +3812,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                special << "trim,";
                        if (opts.find("viewport") != opts.end())
                                special << "viewport=" << opts["viewport"] << ',';
-                       if (opts.find("totalheight") != opts.end())
-                               special << "totalheight=" << opts["totalheight"] << ',';
+                       if (opts.find("height") != opts.end())
+                               special << "height=" << opts["height"] << ',';
                        if (opts.find("type") != opts.end())
                                special << "type=" << opts["type"] << ',';
                        if (opts.find("ext") != opts.end())
@@ -3485,7 +3876,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               else if (t.cs() == "makeindex" || t.cs() == "maketitle") {
+               else if (t.cs() == "makeindex"
+                        || ((t.cs() == "maketitle" || t.cs() == context.textclass.titlename())
+                            && context.textclass.titletype() == TITLE_COMMAND_AFTER)) {
                        if (preamble.titleLayoutFound()) {
                                // swallow this
                                skip_spaces_braces(p);
@@ -3526,18 +3919,29 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                if (t.cs() == "listof") {
                        p.skip_spaces(true);
-                       string const name = p.get_token().cs();
+                       string const name = p.verbatim_item();
                        if (context.textclass.floats().typeExist(name)) {
                                context.check_layout(os);
                                begin_inset(os, "FloatList ");
                                os << name << "\n";
                                end_inset(os);
-                               p.get_token(); // swallow second arg
+                               p.verbatim_item(); // swallow second arg
                        } else
                                output_ert_inset(os, "\\listof{" + name + "}", context);
                        continue;
                }
 
+               if (t.cs() == "theendnotes"
+                  || (t.cs() == "printendnotes"
+                      && p.next_token().asInput() != "*"
+                      && !p.hasOpt())) {
+                       context.check_layout(os);
+                       begin_inset(os, "FloatList endnote\n");
+                       end_inset(os);
+                       skip_spaces_braces(p);
+                       continue;
+               }
+
                if ((where = is_known(t.cs(), known_text_font_families))) {
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
                                context, "\\family", context.font.family,
@@ -3545,14 +3949,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               if ((where = is_known(t.cs(), known_text_font_series))) {
+               // beamer has a \textbf<overlay>{} inset
+               if (!p.hasOpt("<") && (where = is_known(t.cs(), known_text_font_series))) {
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
                                context, "\\series", context.font.series,
                                known_coded_font_series[where - known_text_font_series]);
                        continue;
                }
 
-               if ((where = is_known(t.cs(), known_text_font_shapes))) {
+               // beamer has a \textit<overlay>{} inset
+               if (!p.hasOpt("<") && (where = is_known(t.cs(), known_text_font_shapes))) {
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
                                context, "\\shape", context.font.shape,
                                known_coded_font_shapes[where - known_text_font_shapes]);
@@ -3631,9 +4037,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               if (t.cs() == "uuline" || t.cs() == "uwave"
+               // beamer has an \emph<overlay>{} inset
+               if ((t.cs() == "uuline" || t.cs() == "uwave"
                        || t.cs() == "emph" || t.cs() == "noun"
-                       || t.cs() == "xout") {
+                       || t.cs() == "xout") && !p.hasOpt("<")) {
                        context.check_layout(os);
                        os << "\n\\" << t.cs() << " on\n";
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
@@ -3644,11 +4051,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") {
+               if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted" || t.cs() == "lyxobjdeleted"
+                   || t.cs() == "lyxdisplayobjdeleted" || t.cs() == "lyxudisplayobjdeleted") {
                        context.check_layout(os);
+                       string initials;
+                       if (p.hasOpt()) {
+                               initials = p.getArg('[', ']');
+                       }
                        string name = p.getArg('{', '}');
                        string localtime = p.getArg('{', '}');
-                       preamble.registerAuthor(name);
+                       preamble.registerAuthor(name, initials);
                        Author const & author = preamble.getAuthor(name);
                        // from_asctime_utc() will fail if LyX decides to output the
                        // time in the text language.
@@ -3664,7 +4076,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                os << "\n\\change_deleted ";
                        os << author.bufferId() << ' ' << ptime << '\n';
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
-                       bool dvipost    = LaTeXPackages::isAvailable("dvipost");
                        bool xcolorulem = LaTeXPackages::isAvailable("ulem") &&
                                          LaTeXPackages::isAvailable("xcolor");
                        // No need to test for luatex, since luatex comes in
@@ -3677,9 +4088,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        preamble.registerAutomaticallyLoadedPackage("pdfcolmk");
                                }
                        } else {
-                               if (dvipost) {
-                                       preamble.registerAutomaticallyLoadedPackage("dvipost");
-                               } else if (xcolorulem) {
+                               if (xcolorulem) {
                                        preamble.registerAutomaticallyLoadedPackage("ulem");
                                        preamble.registerAutomaticallyLoadedPackage("xcolor");
                                }
@@ -3841,21 +4250,37 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               // handle refstyle first to catch \eqref which can also occur
-               // without refstyle. Only recognize these commands if
+               // Handle refstyle first in order to to catch \eqref, because this
+               // can also occur without refstyle. Only recognize these commands if
                // refstyle.sty was found in the preamble (otherwise \eqref
                // and user defined ref commands could be misdetected).
-               if ((where = is_known(t.cs(), known_refstyle_commands))
+               // We uncapitalize the input in order to catch capitalized commands
+               // such as \Eqref.
+               if ((where = is_known(uncapitalize(t.cs()), known_refstyle_commands))
                     && preamble.refstyle()) {
+                       string const cap = isCapitalized(t.cs()) ? "true" : "false";
+                       string plural = "false";
+                       // Catch the plural option [s]
+                       if (p.hasOpt()) {
+                               string const opt = p.getOpt();
+                               if (opt == "[s]")
+                                       plural = "true";
+                               else {
+                                       // LyX does not yet support other optional arguments of ref commands
+                                       output_ert_inset(os, t.asInput() + opt + "{" +
+                                              p.verbatim_item() + '}', context);
+                                       continue;
+                               }
+                       }
                        context.check_layout(os);
                        begin_command_inset(os, "ref", "formatted");
                        os << "reference \"";
                        os << known_refstyle_prefixes[where - known_refstyle_commands]
                           << ":";
-                       os << convert_literate_command_inset_arg(p.verbatim_item())
+                       os << convert_literate_command_inset_arg(p.getArg('{', '}'))
                           << "\"\n";
-                       os << "plural \"false\"\n";
-                       os << "caps \"false\"\n";
+                       os << "plural \"" << plural << "\"\n";
+                       os << "caps \"" << cap << "\"\n";
                        os << "noprefix \"false\"\n";
                        end_inset(os);
                        preamble.registerAutomaticallyLoadedPackage("refstyle");
@@ -3884,8 +4309,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        preamble.registerAutomaticallyLoadedPackage("prettyref");
                        } else {
                                // LyX does not yet support optional arguments of ref commands
-                               output_ert_inset(os, t.asInput() + '[' + opt + "]{" +
-                                      p.verbatim_item() + '}', context);
+                               output_ert_inset(os, t.asInput() + opt + "{" +
+                                                p.verbatim_item() + '}', context);
                        }
                        continue;
                }
@@ -4049,13 +4474,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        }
                        string keys, pretextlist, posttextlist;
                        if (qualified) {
-                               map<string, string> pres, posts, preslit, postslit;
+                               vector<pair<string, string>> pres, posts, preslit, postslit;
                                vector<string> lkeys;
                                // text before the citation
                                string lbefore, lbeforelit;
                                // text after the citation
                                string lafter, lafterlit;
-                               string lkey;    
+                               string lkey;
                                pair<bool, string> laft, lbef;
                                while (true) {
                                        get_cite_arguments(p, true, lbefore, lafter);
@@ -4066,7 +4491,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                laft = convert_latexed_command_inset_arg(lafter);
                                                literal |= !laft.first;
                                                lafter = laft.second;
-                                               lafterlit = subst(lbefore, "\n", " ");
+                                               lafterlit = subst(lafter, "\n", " ");
                                        }
                                        if (!lbefore.empty()) {
                                                lbefore.erase(0, 1);
@@ -4091,14 +4516,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        lkey = p.getArg('{', '}');
                                        if (lkey.empty())
                                                break;
-                                       if (!lbefore.empty()) {
-                                               pres.insert(make_pair(lkey, lbefore));
-                                               preslit.insert(make_pair(lkey, lbeforelit));
-                                       }
-                                       if (!lafter.empty()) {
-                                               posts.insert(make_pair(lkey, lafter));
-                                               postslit.insert(make_pair(lkey, lafterlit));
-                                       }
+                                       pres.push_back(make_pair(lkey, lbefore));
+                                       preslit.push_back(make_pair(lkey, lbeforelit));
+                                       posts.push_back(make_pair(lkey, lafter));
+                                       postslit.push_back(make_pair(lkey, lafterlit));
                                        lkeys.push_back(lkey);
                                }
                                keys = convert_literate_command_inset_arg(getStringFromVector(lkeys));
@@ -4109,12 +4530,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                for (auto const & ptl : pres) {
                                        if (!pretextlist.empty())
                                                pretextlist += '\t';
-                                       pretextlist += ptl.first + " " + ptl.second;
+                                       pretextlist += ptl.first;
+                                       if (!ptl.second.empty())
+                                               pretextlist += " " + ptl.second;
                                }
                                for (auto const & potl : posts) {
                                        if (!posttextlist.empty())
                                                posttextlist += '\t';
-                                       posttextlist += potl.first + " " + potl.second;
+                                       posttextlist += potl.first;
+                                       if (!potl.second.empty())
+                                               posttextlist += " " + potl.second;
                                }
                        } else
                                keys = convert_literate_command_inset_arg(p.verbatim_item());
@@ -4303,6 +4728,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                // so simply skip it.
                                parse_text_snippet(p, FLAG_ITEM, false, context);
                        }
+                       eat_whitespace(p, os, context, true);
                        continue;
                }
 
@@ -4375,7 +4801,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                if ((where = is_known(t.cs(), known_quotes))) {
                        context.check_layout(os);
                        begin_inset(os, "Quotes ");
-                       os << known_coded_quotes[where - known_quotes];
+                       string quotetype = known_coded_quotes[where - known_quotes];
+                       // try to make a smart guess about the side
+                       Token const prev = p.prev_token();
+                       bool const opening = (prev.cat() != catSpace && prev.character() != 0
+                                       && prev.character() != '\n' && prev.character() != '~');
+                       quotetype = guessQuoteStyle(quotetype, opening);
+                       os << quotetype;
                        end_inset(os);
                        // LyX adds {} after the quote, so we have to eat
                        // spaces here if there are any before a possible
@@ -4386,7 +4818,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                if ((where = is_known(t.cs(), known_sizes)) &&
-                        context.new_layout_allowed) {
+                       context.new_layout_allowed) {
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.size = known_coded_sizes[where - known_sizes];
@@ -4551,13 +4983,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        continue;
                }
 
-               if (t.cs() == "textquotedbl") {
-                       context.check_layout(os);
-                       os << "\"";
-                       skip_braces(p);
-                       continue;
-               }
-
                if (t.cs() == "_" || t.cs() == "&" || t.cs() == "#"
                            || t.cs() == "$" || t.cs() == "{" || t.cs() == "}"
                            || t.cs() == "%" || t.cs() == "-") {
@@ -4638,7 +5063,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                if (t.cs() == "input" || t.cs() == "include"
                    || t.cs() == "verbatiminput"
-                   || t.cs() == "lstinputlisting") {
+                   || t.cs() == "lstinputlisting"
+                   || t.cs() == "inputminted") {
                        string name = t.cs();
                        if (name == "verbatiminput"
                            && p.next_token().asInput() == "*")
@@ -4652,6 +5078,43 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                literal = !oa.first;
                                if (literal)
                                        lstparams = subst(lstparams, "\n", " ");
+                               else
+                                       lstparams = oa.second;
+                       } else if (name == "inputminted") {
+                               name = "lstinputlisting";
+                               string const lang = p.getArg('{', '}');
+                               if (lang != "tex") {
+                                       string cmd = "\\inputminted{" + lang + "}{";
+                                       cmd += p.getArg('{', '}') + "}";
+                                       output_ert_inset(os, cmd, context);
+                                       continue;
+                               }
+                               if (prefixIs(minted_nonfloat_caption, "[t]")) {
+                                       minted_nonfloat_caption.erase(0,3);
+                                       // extract label and caption from the already produced LyX code
+                                       vector<string> nfc = getVectorFromString(minted_nonfloat_caption, "\n");
+                                       string const caption = nfc.front();
+                                       string label;
+                                       vector<string>::iterator it =
+                                               find(nfc.begin(), nfc.end(), "LatexCommand label");
+                                       if (it != nfc.end()) {
+                                               ++it;
+                                               if (it != nfc.end())
+                                                       label = *it;
+                                               label = support::split(label, '"');
+                                               label.pop_back();
+                                       }
+                                       minted_nonfloat_caption.clear();
+                                       lstparams = "caption=" + caption;
+                                       if (!label.empty())
+                                               lstparams += ",label=" + label;
+                                       pair<bool, string> oa = convert_latexed_command_inset_arg(lstparams);
+                                       literal = !oa.first;
+                                       if (literal)
+                                               lstparams = subst(lstparams, "\n", " ");
+                                       else
+                                               lstparams = oa.second;
+                               }
                        }
                        string lit = literal ? "\"true\"" : "\"false\"";
                        string filename(normalize_filename(p.getArg('{', '}')));
@@ -4865,6 +5328,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                bibliographystyle.clear();
                        }
                        os << "options " << '"' << BibOpts << '"' << "\n";
+                       if (p.getEncoding() != preamble.docencoding) {
+                               Encoding const * const enc = encodings.fromIconvName(
+                                       p.getEncoding(), Encoding::inputenc, true);
+                               if (!enc) {
+                                       cerr << "Unknown bib encoding " << p.getEncoding()
+                                            << ". Ignoring." << std::endl;
+                               } else
+                                       os << "encoding " << '"' << enc->name() << '"' << "\n";
+                       }
                        end_inset(os);
                        continue;
                }
@@ -4905,6 +5377,23 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        os << "options " << '"' << BibOpts << '"' << "\n";
                        if (!bbloptions.empty())
                                os << "biblatexopts " << '"' << bbloptions << '"' << "\n";
+                       if (!preamble.bibencoding.empty()) {
+                               Encoding const * const enc = encodings.fromLaTeXName(
+                                       preamble.bibencoding, Encoding::inputenc, true);
+                               if (!enc) {
+                                       cerr << "Unknown bib encoding " << preamble.bibencoding
+                                            << ". Ignoring." << std::endl;
+                               } else
+                                       os << "encoding " << '"' << enc->name() << '"' << "\n";
+                       }
+                       string bibfileencs;
+                       for (auto const & bf : preamble.biblatex_encodings) {
+                               if (!bibfileencs.empty())
+                                       bibfileencs += "\t";
+                               bibfileencs += bf;
+                       }
+                       if (!bibfileencs.empty())
+                               os << "file_encodings " << '"' << bibfileencs << '"' << "\n";
                        end_inset(os);
                        need_commentbib = false;
                        continue;
@@ -5319,6 +5808,58 @@ 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);
+                       context.latexparam.clear();
+                       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)
@@ -5480,7 +6021,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                // and math commands may be invalid (bug 6797)
                string name = t.asInput();
                // handle the dingbats, cyrillic and greek
-               if (name == "\\ding" || name == "\\textcyr" ||
+               if (name == "\\textcyr")
+                       name = "\\textcyrillic";
+               if (name == "\\ding" || name == "\\textcyrillic" ||
                    (name == "\\textgreek" && !preamble.usePolyglossia()))
                        name = name + '{' + p.getArg('{', '}') + '}';
                // handle the ifsym characters
@@ -5607,8 +6150,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                p.get_token();  // Eat '*'
                                name += '*';
                        }
-                       if (!parse_command(name, p, os, outer, context))
+                       if (!parse_command(name, p, os, outer, context)) {
                                output_ert_inset(os, name, context);
+                               // Try to handle options of unknown commands:
+                               // Look if we have optional arguments,
+                               // and if so, put the brackets in ERT.
+                               while (p.hasOpt()) {
+                                       p.get_token(); // eat '['
+                                       output_ert_inset(os, "[", context);
+                                       os << parse_text_snippet(p, FLAG_BRACK_LAST, outer, context);
+                                       output_ert_inset(os, "]", context);
+                               }
+                       }
                }
        }
 }
@@ -5706,6 +6259,14 @@ void check_comment_bib(ostream & os, Context & context)
        }
        if (!bibfiles.empty())
                os << "bibfiles " << '"' << bibfiles << '"' << "\n";
+       string bibfileencs;
+       for (auto const & bf : preamble.biblatex_encodings) {
+               if (!bibfileencs.empty())
+                       bibfileencs += "\t";
+               bibfileencs += bf;
+       }
+       if (!bibfileencs.empty())
+               os << "file_encodings " << '"' << bibfileencs << '"' << "\n";
        end_inset(os);// Bibtex
        os << "\\end_layout\n";
        end_inset(os);// Note